Introduction

cone rod homeobox (CRX) mutations can cause dominant Leber congenital amaurosis (LCA), a genetic disorder that can lead to blindness. Normally LCA is caused by recessive genes, but on rarer cases, it can be cause by dominant genes, such as CRX. We are look at Bulk RNA-seq data from retinal organoids derived from induced pluripotent stem cells (iPSCs) which is itself take from a LCA patient with CRX-I138fs mutation and their healthy parent. This mutation occurs in the transactivation domain, and so the effect of the mutation ist that CRX is not transcribed. The data source is GSE152939. The author is trying to show that a deno-associated virus gene therapy can restore RNA expression of CRX. However, although this study is about gene therapy, the data that show rescued expression is in single cell RNA-seq data, which is not in the scope of this report, so it was not included. The data was collected from 6 retinal organoids, for 90 days, 125 days, 150 days and 200 day after the iPSCs start to differentiate; 3 of the organoids were from LCA patient, and 3 organoids were from their healthy parent. (Kruczek et al. 2021)

Note that the vast majority of code in this report are take from BCB420 lecture (Isserlin 2022c, 2022a, 2022d, 2022b) with minor modification to make it appropriate for my data.

Note 2: I cannot put bibtex inline citatation within the codechunk, so it is left at the end of the code chunk, and sometimes there is a figure, so the figure legend/caption may be directly followed by the inline citation

Assignment 1 processing

Lets us first do all the processing that was done in assignment 1

source("./assignment_functions.r")
supplementaryFiles <- downloadSupplementaryFiles("GSE152939")
CRX_ExperimentRawCount <- readFile(supplementaryFiles)
CRX_ExperimentRawCountFiltered <- filterOutLowCount(CRX_ExperimentRawCount)
normalized_counts <- normalizeTheCounts(CRX_ExperimentRawCountFiltered)

(Isserlin 2022c, 2022a; Davis and Meltzer 2007; Durinck et al. 2009; Chen, Lun, and Smyth 2016)

ensemblDataSet <- getEnsemblbiomart()

(Isserlin 2022c, 2022a; Durinck et al. 2009)

normalized_counts_annot <- mapTheData(CRX_ExperimentRawCountFiltered, normalized_counts, ensemblDataSet)

(Isserlin 2022c, 2022a; Durinck et al. 2009)

saveRDS(normalized_counts_annot, file = "normalized_counts_annot.rds")
# Apprently there is a dupliate emseblem id
normalized_count_data <- normalized_counts_annot[!duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
normalized_counts_annot[duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
NA

Table 1a: The duplicate gene that was removed.

normalized_counts_annot[which(normalized_counts_annot$ensembl_gene_id == 'ENSG00000254876'), ]

Table 1b: showing the two gene has the same ensembl gene id, but not exactly the same hgnc symbol

I find the one that is consider as duplicate, then find the one that has the same enmselble gene id as it. For some reason this ensembl gene id maps to 2 hgnc symbols, SUGT1P4-STRA6LP and STRA6LP, even from the name it look like the same gene

This gene was has a expression of 10 times greater than the 2nd highest expressed gene, it seesm like it may be some error, so I removed it.

normalized_count_cleaned <- normalized_count_data[normalized_count_data$ensembl_gene_id != 'ENSG00000156508', ]

Heatmap for all genes

first make a heat map matrix (the input thing use to generate a heat map)


heatmap_matrix <- normalized_count_cleaned[,
                        3:ncol(normalized_count_cleaned)]
rownames(heatmap_matrix) <- normalized_count_cleaned$ensembl_gene_id
colnames(heatmap_matrix) <- colnames(normalized_count_cleaned[, 3:ncol(normalized_count_cleaned)])
heatmap_matrix
saveRDS(heatmap_matrix, file = "heatmap_list.rds")

Table 2: The first 1000 row of the filtered normalized data for all samples, ordered by ensembl id

(Isserlin 2022d)

heatmap_matrix <- readRDS("./heatmap_list.rds")

then generate a heat map

library(ComplexHeatmap)
library(circlize)
if(min(heatmap_matrix) == 0){
  heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))

} else {
  heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}

Heatmap(as.matrix(heatmap_matrix[1:1000,]),
      show_row_dend = TRUE, show_column_dend = TRUE,
      col=heatmap_col, show_column_names = TRUE,
      show_row_names = FALSE, show_heatmap_legend = TRUE,
      raster_resize_mat = TRUE, heatmap_legend_param = list(title = "Initial Heatmap"))

Figure 1: Initial heatmap for differential expression with the first 1000 genes, order by ensembl id. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)

Now with row normalization (i.e. the kind with subtract the mean divided by standard deviation) we can see each gene better

heatmap_matrix <- t(scale(t(heatmap_matrix)))

if(min(heatmap_matrix) == 0){
heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}

Heatmap(as.matrix(heatmap_matrix[1:1000,]),
      show_row_dend = TRUE,show_column_dend = TRUE,
      col=heatmap_col,show_column_names = TRUE,
      show_row_names = FALSE,show_heatmap_legend = TRUE, heatmap_legend_param = list(title = " Heatmap with row normalization"))

Figure 2: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)

you can see that the samples are more similar to each other at the same time point than whether or not it is control, I think that is reasonable since many gene get simulated and repressed during development, so the genes that are on is different from one development time point, to another.

Heatmap for tophits using Limma

We will be fitting the data to the a linear model and using empirical bayes to calculate p-values. Then we will using Benjamni-Hochberg for multiple hypothesis testing, for correcting our p-values.

Simple Model

The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.

samples <- data.frame(lapply(colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)], FUN=function(x){unlist(strsplit(x, split = "\\_"))[c(1, 2, 3)]}))
samples
colnames(samples) <- colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)]
rownames(samples) <- c("condition", "time","patient")
samples <- data.frame(t(samples))

model_design <- model.matrix(~ samples$condition+samples$time)

expressionMatrix <- as.matrix(normalized_count_data[,3:ncol(CRX_ExperimentRawCountFiltered)])
rownames(expressionMatrix) <-
  normalized_count_data$ensembl_gene_id
colnames(expressionMatrix) <-
  colnames(normalized_count_data)[3:ncol(CRX_ExperimentRawCountFiltered)]
minimalSet <- Biobase::ExpressionSet(assayData=expressionMatrix)

fit <- limma::lmFit(minimalSet, model_design)
fit2 <- limma::eBayes(fit,trend=TRUE)

topfit <- limma::topTable(fit2,
                   coef=ncol(model_design),
                   adjust.method = "BH",
                   number = nrow(expressionMatrix))
#merge hgnc names to topfit table
output_hits <- merge(normalized_count_data[,1:2],
                     topfit,
                     by.y=0,by.x=1,
                     all.y=TRUE)
#sort by pvalue
output_hits <- output_hits[order(output_hits$P.Value),]

Table 3: Showing for ecah sample, which organoid, at what time, and what condition it is

(Isserlin 2022d; Huber et al. 2015; Ritchie et al. 2015)

knitr::kable(output_hits[1:10,1:7],type="html",row.names = FALSE)
ensembl_gene_id hgnc_symbol logFC AveExpr t P.Value adj.P.Val
ENSG00000225840 -751.52636 278.308984 -34.89691 0 0
ENSG00000243199 -14.97061 6.890542 -30.68555 0 0
ENSG00000280614 -1251.17742 468.102238 -21.35881 0 0
ENSG00000280800 -1251.17742 468.102238 -21.35881 0 0
ENSG00000281181 -1251.17742 468.102238 -21.35881 0 0
ENSG00000276043 UHRF1 19.96635 8.419313 21.03885 0 0
ENSG00000109991 P2RX3 15.42168 4.861319 20.92939 0 0
ENSG00000151615 POU4F2 22.61264 6.640604 20.68397 0 0
ENSG00000147432 CHRNB3 8.65588 2.777748 20.59896 0 0
ENSG00000076003 MCM6 44.11041 47.070545 18.54065 0 0

Table 4: Show the limma output after fitting the data to the simple model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0

(Isserlin 2022d; Xie 2021)

This is how many genes that have pvalue under 0.05

length(which(output_hits$P.Value < 0.05))
[1] 8178

(Isserlin 2022d)

This is how many genes that still pvalue of less than 0.05 after adjustment

length(which(output_hits$adj.P.Val < 0.05))
[1] 6174

(Isserlin 2022d)

Organoid Model

The organoid model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.

model_design_org <- model.matrix(
  ~ samples$patient + samples$condition + samples$time)

fit_pat <- limma::lmFit(minimalSet, model_design_org)

(Isserlin 2022d; Xie 2021)

Change the model, fit the data to the model, then apply empircal bayes , then correct with BH

fit_org <- limma::lmFit(minimalSet, model_design_org)
fit2_org <- limma::eBayes(fit_pat,trend=TRUE)

topfit_org <- limma::topTable(fit2_pat,
                   coef=ncol(model_design_org),
                   adjust.method = "BH",
                   number = nrow(expressionMatrix))

#merge hgnc names to topfit table
output_hits_org <- merge(normalized_count_data[,1:2],
                         topfit_org, by.y=0, by.x=1, all.y=TRUE)
#sort by pvalue
output_hits_org <- output_hits_org[order(output_hits_org$P.Value),]

(Isserlin 2022d; Ritchie et al. 2015)

knitr::kable(output_hits_org[1:10,1:7],type="html",row.names = FALSE)
ensembl_gene_id hgnc_symbol logFC AveExpr t P.Value adj.P.Val
ENSG00000225840 -751.52636 278.308984 -34.77848 0 0
ENSG00000243199 -14.97061 6.890542 -29.37584 0 0
ENSG00000151615 POU4F2 22.61264 6.640604 21.07068 0 0
ENSG00000147432 CHRNB3 8.65588 2.777748 21.01946 0 0
ENSG00000276043 UHRF1 19.96635 8.419313 20.97652 0 0
ENSG00000109991 P2RX3 15.42168 4.861319 20.54571 0 0
ENSG00000280614 -1251.17742 468.102238 -20.49586 0 0
ENSG00000280800 -1251.17742 468.102238 -20.49586 0 0
ENSG00000281181 -1251.17742 468.102238 -20.49586 0 0
ENSG00000165646 SLC18A2 25.24788 8.885919 19.54160 0 0

Table 5: Show the limma output after fitting the data to the organoid model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p-value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0

(Isserlin 2022d; Xie 2021)

length(which(output_hits_org$P.Value < 0.05))
[1] 7838

(Isserlin 2022d)

length(which(output_hits_org$adj.P.Val < 0.05))
[1] 5677

(Isserlin 2022d)

Comparing both models

There is actually less, but let us look at it from a graph

simple_model_pvalues <- data.frame(ensembl_id =
  output_hits$ensembl_gene_id,
  simple_pvalue=output_hits$adj.P.Val)

org_model_pvalues <-  data.frame(ensembl_id =
  output_hits_org$ensembl_gene_id,
  organoid_pvalue = output_hits_org$adj.P.Val)

two_models_pvalues <- merge(simple_model_pvalues,
  org_model_pvalues,by.x=1,by.y=1)

two_models_pvalues$colour <- "black"

two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05] <- "orange"

two_models_pvalues$colour[
  two_models_pvalues$organoid_pvalue<0.05] <- "blue"

two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05 &
  two_models_pvalues$organoid_pvalue<0.05] <- "red"

plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$organoid_pvalue,
     col = two_models_pvalues$colour,
     xlab = "Simple model corrected p-values",
     ylab ="Organoid model corrected p-values",
     main="Simple vs Organoid Limma",
     xlim = c(0, 0.1),
     ylim = c(0, 0.1))

Figure 3: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)

To determine which model is more appropriate, we take a look at p-value of the gene of interest, CRX.

ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
  which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
                            ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$organoid_pvalue,
     col = two_models_pvalues$colour,
     xlab = "simple model corrected p-values",
     ylab ="organoid model corrected  p-values",
     main="Simple vs Organoid Limma",
     xlim = c(0, 0.1),
     ylim = c(0, 0.1))
points(two_models_pvalues[which(
  two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
       pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
       fill=c("red","grey"),cex = 0.7)

Figure 4: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting only the gene of interest, CRX as red (Isserlin 2022d)

The simple model is better, I believe that is the because there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.

Heatmap from limma

Let us look at the heatmap of only the top hits (by corrected pvalue), in the simple model

top_hits <- output_hits$ensembl_gene_id[
  output_hits$adj.P.Val<0.05]

heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))


if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_atrix_tophits)),
                             c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
      max(heatmap_matrix_tophits)), c("blue", "white", "red"))
  }

Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
                           cluster_rows = TRUE,
                           cluster_columns = TRUE,
                               show_row_dend = TRUE,
                               show_column_dend = TRUE,
                               col=heatmap_col,
                               show_column_names = TRUE,
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE, 
        heatmap_legend_param = list(title = "Heatmap from Limma")
                               )

Figure 5: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes by corrected p-value using the simple model in limma. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)

Let us not cluster the genes but group controls and experiments together

top_hits <- output_hits$ensembl_gene_id[
  output_hits$adj.P.Val<0.05]

heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))

heatmap_matrix_tophits <- heatmap_matrix_tophits[,
c(
  grep(colnames(heatmap_matrix_tophits), pattern = "\\CRXLCA"),
  grep(colnames(heatmap_matrix_tophits), pattern = "\\Control")
 )
]

if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
                             c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
                    max(heatmap_matrix_tophits)),
                    c("blue", "white", "red"))
}

Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
                           cluster_rows = TRUE,
                           cluster_columns = FALSE,
                               show_row_dend = TRUE,
                               show_column_dend = TRUE,
                               col=heatmap_col,
                               show_column_names = TRUE,
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE,
                               heatmap_legend_param = list(title = "Limma Heatmap without clustering"))

Figure 6: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes based on corrected p-value using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)

We can clearly tell that the test condition do not look like each other. The controls also do not look like each other.

Then again look at top hits with less than 0.01 pvalue

top_hits <- output_hits$ensembl_gene_id[
  output_hits$adj.P.Val < 0.01]

heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix) %in% top_hits),])))

heatmap_matrix_tophits <- heatmap_matrix_tophits[,
       c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
         grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]

nrow(heatmap_matrix_tophits)
[1] 3726
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
                             c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
                              max(heatmap_matrix_tophits)),
                             c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
                cluster_rows = TRUE,  show_row_dend = TRUE,
                cluster_columns = FALSE,show_column_dend = FALSE,
                col=heatmap_col,show_column_names = TRUE,
                show_row_names = FALSE,show_heatmap_legend = TRUE, heatmap_legend_param = list(title = "Limma Heatmap, no cluster, 0.1"))

Figure 7: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for hits with corrected p-value smaller than 0.01 using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)

Heatmap with tophits from edgeR

Simple Model

The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.

filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d <- edgeR::estimateDisp(d, model_design)
fit <- edgeR::glmQLFit(d, model_design)

(Isserlin 2022d; Chen, Lun, and Smyth 2016)

qlf.pos_vs_neg <- edgeR::glmQLFTest(fit, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
logFC logCPM F PValue FDR
2.911104 3.260820 2259.6310 0 0
4.776156 4.149346 1864.2162 0 0
-5.335662 1.951992 1626.9044 0 0
8.689098 1.065658 1399.1244 0 0
-3.289784 3.956177 998.1517 0 0
-3.903175 3.731926 782.7499 0 0
-1.658192 5.244965 608.3937 0 0
-2.892034 2.697657 582.5644 0 0
-6.520603 4.378680 568.5639 0 0
-3.219553 2.385684 530.9328 0 0
x
BH
x
samples$conditionCRXLCA
x
glm

(Isserlin 2022d; Chen, Lun, and Smyth 2016; Xie 2021)

Even though this say FDR, but since we are using the same multiple hypothesis testing method for both edgeR and limma, they are the same type of corrected values. The first one is gene before correction, and second one is after correction

qlf_output_hits <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
                           n = nrow(normalized_count_data))
length(which(qlf_output_hits$table$PValue < 0.05))
[1] 6909
length(which(qlf_output_hits$table$FDR < 0.05))
[1] 4652

(Isserlin 2022d; Chen, Lun, and Smyth 2016)

Organoid model

The organoid Model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.

filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d_org <- edgeR::estimateDisp(d, model_design_org)
fit_org <- edgeR::glmQLFit(d_org, model_design_org)

(Isserlin 2022d; Chen, Lun, and Smyth 2016)

qlf.pos_vs_neg <- edgeR::glmQLFTest(fit_org, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
logFC logCPM F PValue FDR
2.911666 3.260502 1983.9723 0 0
-5.334781 1.952266 1785.2164 0 0
4.779307 4.149185 1630.2907 0 0
8.689482 1.065724 1374.1628 0 0
-3.299374 3.956445 947.1039 0 0
-3.907342 3.732095 741.8239 0 0
-1.662314 5.244975 663.5731 0 0
-6.552258 4.378782 533.3569 0 0
-2.891894 2.697840 516.0534 0 0
-6.594928 -1.391819 495.8771 0 0
x
BH
x
samples$conditionCRXLCA
x
glm

(Isserlin 2022d; Xie 2021; Chen, Lun, and Smyth 2016)

The first one is gene before correction, and second one is after correction

qlf_output_hits_org <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
                           n = nrow(normalized_count_data))
length(which(qlf_output_hits_org$table$PValue < 0.05))
[1] 6556
length(which(qlf_output_hits_org$table$FDR < 0.05))
[1] 4222

(Isserlin 2022d; Chen, Lun, and Smyth 2016)

Compare 2 model from edgeR

simple_model_pvalues_qlf <- data.frame(ensembl_id =
  rownames(qlf_output_hits$table),
  simple_pvalue = qlf_output_hits$table$FDR)

org_model_pvalues_qlf <-  data.frame(ensembl_id =
  rownames(qlf_output_hits_org$table),
  organoid_pvalue = qlf_output_hits_org$table$FDR)

two_models_pvalues <- merge(simple_model_pvalues_qlf,
  org_model_pvalues_qlf, by.x=1, by.y=1)

two_models_pvalues$colour <- "black"

two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05] <- "orange"

two_models_pvalues$colour[
  two_models_pvalues$organoid_pvalue<0.05] <- "blue"

two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05 &
  two_models_pvalues$organoid_pvalue<0.05] <- "red"

plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$organoid_pvalue,
     col = two_models_pvalues$colour,
     xlab = "Simple model corrected p-values",
     ylab ="Organoid model corrected p-values",
     main="Simple vs Organoid edgeR",
     xlim=c(0, 0.1),
     ylim=c(0, 0.1))

Figure 8: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)

The majority of genes that have less than 0.05 p-value in either model, has less than 0.05 p-value in both model.

To determine which model is more appropriate, we take a look at p=value of the gene of interest, CRX.

ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
  which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
                            ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$organoid_pvalue,
     col = two_models_pvalues$colour,
     xlab = "simple model corrected p-values",
     ylab ="organoid model corrected p-values",
     main="Simple vs Organoid Limma",
     xlim = c(0, 0.01),
     ylim = c(0, 0.01))
points(two_models_pvalues[which(
  two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
       pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
       fill=c("red","grey"),cex = 0.7)

Figure 9: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.01. Highlighting only the gene of interest, CRX (Isserlin 2022d)

The simple model is better, I believe this is still due to the same reason that, there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.

Compare limma with quasi-likelihood

qlf_simple_model_pvalues <- data.frame(
          ensembl_id = rownames(qlf_output_hits$table),
          qlf_simple_pvalue = qlf_output_hits$table$FDR)

limma_simple_model_pvalues <-  data.frame(
          ensembl_id = output_hits$ensembl_gene_id,
          limma_simple_pvalue = output_hits$adj.P.Val)

two_models_pvalues <- merge(qlf_pat_model_pvalues,
                            limma_pat_model_pvalues,
                            by.x=1,by.y=1)

two_models_pvalues$colour <- "black"

two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
                          <0.05] <- "orange"

two_models_pvalues$colour[two_models_pvalues$limma_simple_pvalue
                          <0.05] <- "blue"

two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
                          <0.05 &
two_models_pvalues$limma_simple_pvalue<0.05] <- "red"

plot(two_models_pvalues$qlf_simple_pvalue,
     two_models_pvalues$limma_simple_pvalue,
     col = two_models_pvalues$colour,
     xlab = "QLF simple model adjusted p-values",
     ylab ="Limma simple model adjusted p-values",
     main="QLF vs Limma",
     xlim = c(0, 1),
     ylim = c(0, 1))

Figure 10: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)

For some reason, this is all over the place

ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
  which(normalized_count_data$hgnc_symbol == "CRX")]

two_models_pvalues$colour <- "grey"

two_models_pvalues$colour[two_models_pvalues$ensembl_id
                          ==ensembl_of_interest] <- "red"

plot(two_models_pvalues$qlf_simple_pvalue,
     two_models_pvalues$limma_simple_pvalue,
     col = two_models_pvalues$colour,
     xlab = "QLF simple model adjusted p-values",
     ylab ="Limma simple model adjusted p-values",
     main="QLF vs Limma",
     xlim = c(0, 0.02),
     ylim = c(0, 0.02))

points(two_models_pvalues[
  two_models_pvalues$ensembl_id==ensembl_of_interest,2:3],
       pch=24,  col="red", cex=1.5)

Figure 11: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma, zoomed in on 0-0.02. Highlighting only the gene of interest, CRX (Isserlin 2022d)

It looks like QLF is better than limma, and that does make sense, since limma was made for microarray, but I am using bulk RNA-seq data, which edgeR methods are designed for.

Heatmap from edgeR

heatmap, but using tophits from qlf instead of limma

top_hits <- rownames(qlf_output_hits$table)[
  qlf_output_hits$table$FDR<0.05]

heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))

nrow(heatmap_matrix_tophits)
[1] 4651
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
                             c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
                      max(heatmap_matrix_tophits)),
                      c("blue", "white", "red"))
}

Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
                           cluster_rows = TRUE,
                           cluster_columns = TRUE,
                               show_row_dend = TRUE,
                               show_column_dend = TRUE,
                               col=heatmap_col,
                               show_column_names = TRUE,
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE, 
        heatmap_legend_param = list(title = "glmQLFit Heatmap")
                               )

Figure 12: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)

compare to the heatmap from lemma, the day 200 do not cluster together, but the day 150 and day 125 cluster into 2 groups. However all day 90 do cluster together.

then also cluster by control vs experiment

top_hits <- rownames(qlf_output_hits$table)[qlf_output_hits$table$FDR
                                            <0.05]
heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))

heatmap_matrix_tophits<- heatmap_matrix_tophits[, 
c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
  grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]

nrow(heatmap_matrix_tophits)
[1] 4651
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
                             c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
                               max(heatmap_matrix_tophits)),
                             c("blue", "white", "red"))
}

Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
                           cluster_rows = TRUE,
                           cluster_columns = FALSE,
                               show_row_dend = TRUE,
                               show_column_dend = FALSE,
                               col=heatmap_col,
                               show_column_names = TRUE,
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE, 
        heatmap_legend_param = list(title = "glmQLFit Heatmap no cluster"))

Figure 13: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)

Over representation analysis

This is the number of genes upregulated and downregulated (I did check the model design, control was labeled as 0 and experiment/CRX was labeled as 1), so this does mean that that is upregulated and downregulated in experiment.

length(which(qlf_output_hits$table$FDR < 0.05 
             & qlf_output_hits$table$logFC > 0))
[1] 2374
length(which(qlf_output_hits$table$FDR < 0.05 
             & qlf_output_hits$table$logFC < 0))
[1] 2278

(Isserlin 2022b)

I only outputted the first 5000 genes, because g:profiler will crash with too many genes.


qlf_output_hits_withgn <- merge(CRX_ExperimentRawCountFiltered[,1:2], qlf_output_hits, by.x=1, by.y = 0)

qlf_output_hits_withgn[,"rank"] <- -log(qlf_output_hits_withgn$FDR, base =10) * sign(qlf_output_hits_withgn$logFC)

qlf_output_hits_withgn <- qlf_output_hits_withgn[order(qlf_output_hits_withgn$rank),]

upregulated_genes <- qlf_output_hits_withgn$gene.id[
  which(qlf_output_hits_withgn$FDR < 0.05 
             & qlf_output_hits_withgn$logFC > 0)]

downregulated_genes <- qlf_output_hits_withgn$gene.id[
  which(qlf_output_hits_withgn$FDR < 0.05 
             & qlf_output_hits_withgn$logFC < 0)]

first5000_genes <- qlf_output_hits_withgn$gene.id[c(1:5000)]

if (!file.exists("data")) {
 dir.create("data")
}

write.table(x=upregulated_genes,
            file=file.path("data","CRX_upregulated_genes.txt"), sep = "\t",
            row.names = FALSE, col.names = FALSE, quote = FALSE)

write.table(x=downregulated_genes,
            file=file.path("data","CRX_downregulated_genes.txt"),sep = "\t",
            row.names = FALSE, col.names = FALSE, quote = FALSE)

write.table(x=first5000_genes,
            file=file.path("data","CRX_ranked_genelist.txt"),
            sep = "\t",
            row.names = FALSE,
            col.names = FALSE,
            quote = FALSE)

(Isserlin 2022b)

Results of G:profiler

Volcano Plot

qlf_pvalues <- data.frame(
          ensembl_id = rownames(qlf_output_hits$table),
          qlf_pvalues = -log10(qlf_output_hits$table$FDR))

qlf_FC <-  data.frame(
          ensembl_id = rownames(qlf_output_hits$table),
          qlf_FC = qlf_output_hits$table$logFC)

pvalue_FC <- merge(qlf_pvalues,
                            qlf_FC,
                            by.x=1,by.y=1)

pvalue_FC$colour <- "black"

pvalue_FC$colour[which(pvalue_FC$qlf_FC > 0)] <- "blue"

pvalue_FC$colour[which(pvalue_FC$qlf_FC < 0)] <- "red"

plot(pvalue_FC$qlf_pvalues,
     pvalue_FC$qlf_FC,
     col = pvalue_FC$colour,
     xlab = "QLF -log10 FDR",
     ylab ="QLF log fold changes",
     main="Volcano plot",
     xlim = c(0, 10),
     ylim = c(-2, 2))

Figure 14: volcano plot of all genes, zoomed in to -2 to 2 for logFC, and 0 to 10 for FDR to show the charateristic U shape.

There is visual separation of upregulated genes and downregulated gene, we can clearly see the



qlf_pvalues_interest <- data.frame(
          ensembl_id = rownames(qlf_output_hits$table),
          qlf_pvalues = -log10(qlf_output_hits$table$FDR))

qlf_FC_interest <-  data.frame(
          ensembl_id = rownames(qlf_output_hits$table),
          qlf_FC = qlf_output_hits$table$logFC)

pvalue_FC_interest <- merge(qlf_pvalues_interest,
                            qlf_FC_interest,
                            by.x=1,by.y=1)

pvalue_FC_interest$colour <- "grey"

pvalue_FC_interest$colour[pvalue_FC$ensembl_id==ensembl_of_interest] <- "red"

plot(pvalue_FC_interest$qlf_pvalues,
     pvalue_FC_interest$qlf_FC,
     col = pvalue_FC_interest$colour,
     xlab = "QLF -log10 FDR",
     ylab ="QLF log fold changes",
     main="Volcano plot",
     xlim = c(2.5, 3.5),
     ylim = c(-1, 1))

Figure 15: volcano plot of all genes, zoomed in to -1 to 1 for logFC, and 2.5 to 3.5 for FDR so that the gene of interest does not get covered by other genes.

Discussion

We have 3 factors that we can include in our model design. The day that the day was collected, which organoid was it collected from, is it experiment or control that we are collecting from?

We do have to include the last one (experiment vs control), or else we are not getting the differential expression for the question that the authors is asking. (Kruczek et al. 2021). Although at first, the control and experiments do not differ that much, but by day 200 they the controls cluster together more than the organoids, so is the experiments. I believe we can conclude that, by day 200, the difference from CRX, is more than the organoid variability.

Looking at the MDS plot from A1, we can tell that the day it was collected from changes the data by a lot, since the RNA is taken from retinal organoids in development, and gene expression for cell in development changes from stage to stage. Therefore we should include this.

Still look at the MDS plot from A1, which organoid it was collected from does not make much of a difference, this is probably because that all control organoids are from the same person, and all experiment organoids are from the same person.

Questions

  1. Calculate p-values for each of the genes in your expression set. How many genes were significantly differentially expressed? What thresholds did you use and why?

    In the model that does not account for organoid variability, the number of genes that where significantly differentially express was 8178. In the model that account for organoid variability, the number is 7838. From edgeR, we got 6556 genes under threshold.

    I used the threshold of 0.05, because of convention and I do not have additional knowledge (from the original paper or anywhere) about what threshold would give me best results for bulk RNA seq data from retinal organoids derived from iPSC where the testing condition is CRX-LCA and the specific mutation is I138fs48.

  2. Multiple hypothesis testing - correct your p-values using a multiple hypothesis correction method. Which method did you use? And Why? How many genes passed correction?

    I tried using Benjamni - Hochberg from both limma and edgeR to correct the p-value for my genes. The one that I would go with is the BH from edgeR with glmQLFTest in the simple model, since the simple model is slightly better and the result are similar (comparing edgeR and limma) but edgeR is designed bulk RNA-seq and limma is not. For number of genes passed correction, limma gave me 6174 and 5677, for the 2 models respectively, edgeR gave me 4222. I used Benjamni - Hochberg, because that seems like it is the most widely used.

  3. Show the amount of differentially expressed genes using an MA Plot or a Volcano plot. Highlight genes of interest.

    see volcano plot section

  4. Visualize your top hits using a heatmap. Do you conditions cluster together? Explain why or why not.

    My conditions do not cluster together in the sense that the test conditions and the controls do not. However, the days (e.g. expression on day 90 vs expression on day 200 vs expression on day 125/150), do cluster together, I believe this is because during development many genes get expressed and repressed, so that changes in the genes expression due to development overshadows those that are due to the disease (LCA). As well, day 125 and 150 are too close in development, compare to the other two, so there is less of a difference.

  5. What method did you choose and why?

    I choose g:profiler as the method for over representation analysis, since I already have experience using it from the g:profiler assignment (which makes it easier to use) and Professor Isserlin seems to recommend it due to the fact that she asked us to use g:profiler for the homework assignment. As well, the pvalue is shown very clearly on g:profiler with lots of colors.

  6. What annotation data did you use and why? What version of the annotation are you using?

    I used Gene ontology: Biological Process released 2021-12-15, Reactome released 2022-1-3, Wiki Pathways released 2021-12-10. I am using this since this was recommend in the g:profiler assignment.

  7. How many genesets were returned with what thresholds?

    For first 5000 genes on the whole list (I could not ran all 19000 genes, since g:profiler will crash) in a unordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 347 for Gene ontology: Biological Process, 31 for Reactome, 14 for Wiki Pathways

    In the same but ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 255 for Gene ontology: Biological Process, 3 for Reactome, 0 for Wiki Pathways

  8. Run the analysis using the up-regulated set of genes, and the down-regulated set of genes separately. How do these results compare to using the whole list (i.e all differentially expressed genes together vs. the up-regulated and down regulated differentially expressed genes separately)?

    For the downregulated genes unordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 43 for Gene ontology: Biological Process, 8 for Reactome, 3 for Wiki Pathways. For the downregulated genes ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 93 for Gene ontology: Biological Process, 3 for Reactome, 0 for Wiki Pathways. For the upregulated genes unordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 507 for Gene ontology: Biological Process, 35 for Reactome, 12 for Wiki Pathways. For the upregulated genes ordered query, the number of terms return at less than 0.05 pvalue and have between 5-200 genes is 773 for Gene ontology: Biological Process, 0 for Reactome, 0 for Wiki Pathways.

  9. Do the over-representation results support conclusions or mechanism discussed in the original paper?

    It does, the top two term of GO:BP (for terms that have between 5-200 genes) for the downregulated unorder genes is detection of light stimulus and detection of visible light. And the fifth term of the ordered query, is phototransduction.

    As well, the top two term (for terms that have between 5-200 genes) for the first 5000 genes on the whole list is detection of light stimulus and phototransduction. The third term of the ordered query, is phototransduction.

    Furthermore, there are many more term in the down-regulated order query, that is related with photoreceptor.

    However, result from the unordered query with reactome and wiki pathway is not that related to vision.

    Since a mutation in CRX prevents it from being transcribed, the result of up-regulated genes is not in the scope of experiment anyway.

  10. Can you find evidence, i.e. publications, to support some of the results that you see. How does this evidence support your results.

    Yes, in (Hennig, Peng, and Chen 2008), say that CRX is central to the transcription factor network that controls photoreceptor development. In both the whole list ordered in the downregulated genes, photoreception is an important term, this means that in the RNA expression of these genes, phototransduction is not activated due to CRX’s role, which is consistent to the finding presented by that study.

    I believe that detection of light stimulus is also supported by this, since light stimulus is essentially photons, and photoreceptor are activated by photons. However, detection of visible light is a bit more tricky, it could be supported if it is in addition to all light, but the same cannot necessarily be concluded, if it is exclusively visible light. Though given the context that the term detection of light stimulus is given together with detection visible light, it would probably be better to interpret it as based on the RNA expression, the person would have trouble detecting light stimulus and visible light.

    Another consideration is that, I think photoreceptor behave differently when struck by photon from non-visible light, or else if phototransduction can be activated to sufficient degree by these photon, then couldn’t we see non-visible light? (That is of course unlikely, by definition)

    Lastly in the Reactome, we have phototransduction cascade as a top term, which is definitely supported by the study in question.

References

Chen, Yunshun, Aaron A T Lun, and Gordon K Smyth. 2016. “From Reads to Genes to Pathways: Differential Expression Analysis of RNA-Seq Experiments Using Rsubread and the edgeR Quasi-Likelihood Pipeline.” F1000Research 5: 1438. https://doi.org/10.12688/f1000research.8987.2.
Davis, Sean, and Paul Meltzer. 2007. “GEOquery: A Bridge Between the Gene Expression Omnibus (GEO) and BioConductor.” Bioinformatics 14: 1846–47.
Durinck, Steffen, Paul T. Spellman, Ewan Birney, and Wolfgang Huber. 2009. “Mapping Identifiers for the Integration of Genomic Datasets with the r/Bioconductor Package biomaRt.” Nature Protocols 4: 1184–91.
Gu, Zuguang, Roland Eils, and Matthias Schlesner. 2016. “Complex Heatmaps Reveal Patterns and Correlations in Multidimensional Genomic Data.” Bioinformatics.
Gu, Zuguang, Lei Gu, Roland Eils, Matthias Schlesner, and Benedikt Brors. 2014. “Circlize Implements and Enhances Circular Visualization in r.” Bioinformatics 30: 2811–12.
Hennig, Anne K, Guang-Hua Peng, and Shiming Chen. 2008. “Regulation of Photoreceptor Gene Expression by Crx-Associated Transcription Factor Network.” Brain Research 1192: 114–33.
Huber, W., V. J. Carey, R. Gentleman, S. Anders, M. Carlson, B. S. Carvalho, H. C. Bravo, et al. 2015. Orchestrating High-Throughput Genomic Analysis with Bioconductor.” Nature Methods 12 (2): 115–21. http://www.nature.com/nmeth/journal/v12/n2/full/nmeth.3252.html.
Isserlin, Ruth. 2022a. “Bcb420 - Computational Systems Biology Lecture. Lecture 5 - Data Exploration and Identifier Mapping.” https://q.utoronto.ca/courses/248455/files/19306012?module_item_id=3480061.
———. 2022b. “Bcb420 - Computational Systems Biology Lecture. Lecture 7 - Annotation Dataset and Intro to Pathway Analysis.” https://q.utoronto.ca/courses/248455/files/18120892?module_item_id=3210881.
———. 2022c. “Bcb420 - Computational Systems Biology. Lecture 4 - Exploring the Data and Basics of Normalization.” https://q.utoronto.ca/courses/248455/files/19273570?module_item_id=3476594.
———. 2022d. “Bcb420 - Computational Systems Biology. Lecture 6 - Differential Expression.” https://q.utoronto.ca/courses/248455/files/19309003?module_item_id=3480535.
Kruczek, Kamil, Zepeng Qu, James Gentry, Benjamin R Fadl, Linn Gieser, Suja Hiriyanna, Zachary Batz, et al. 2021. “Gene Therapy of Dominant CRX-Leber Congenital Amaurosis Using Patient Stem Cell-Derived Retinal Organoids.” Stem Cell Reports 16 (2): 252–63.
Ritchie, Matthew E, Belinda Phipson, Di Wu, Yifang Hu, Charity W Law, Wei Shi, and Gordon K Smyth. 2015. limma Powers Differential Expression Analyses for RNA-Sequencing and Microarray Studies.” Nucleic Acids Research 43 (7): e47. https://doi.org/10.1093/nar/gkv007.
Xie, Yihui. 2021. Knitr: A General-Purpose Package for Dynamic Report Generation in r. https://yihui.org/knitr/.
LS0tCnRpdGxlOiAnQkNCNDIwIEFzc2lnbm1lbnQgMiBSZXBvcnQ6IERpZmZlcmVudGlhbCBHZW5lIGV4cHJlc3Npb24gYW5kIFByZWxpbWluYXJ5IE9SQScKYXV0aG9yOiAiS2FpIFJlbiBDaGVuIgpzdWJ0aXRsZTogJ0pvdXJuYWwgQXJ0aWNsZTogR2VuZSB0aGVyYXB5IG9mIGRvbWluYW50IENSWC1MZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwogIHVzaW5nIHBhdGllbnQgcmV0aW5hbCBvcmdhbm9pZHMnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMicKICAgIGRmX3ByaW50OiBwYWdlZApiaWJsaW9ncmFwaHk6IGEyY2l0YXRpb24uYmliCmxpbmstY2l0YXRpb25zOiB5ZXMKLS0tCgojIEludHJvZHVjdGlvbgpjb25lIHJvZCBob21lb2JveCAoQ1JYKSBtdXRhdGlvbnMgIGNhbiBjYXVzZSBkb21pbmFudCBMZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwooTENBKSwgYSBnZW5ldGljIGRpc29yZGVyIHRoYXQgY2FuIGxlYWQgdG8gYmxpbmRuZXNzLiBOb3JtYWxseSBMQ0EgaXMgY2F1c2VkCmJ5IHJlY2Vzc2l2ZSBnZW5lcywgYnV0IG9uIHJhcmVyIGNhc2VzLCBpdCBjYW4gYmUgY2F1c2UgYnkgZG9taW5hbnQgZ2VuZXMsIHN1Y2gKYXMgQ1JYLiBXZSBhcmUgbG9vayBhdCBCdWxrIFJOQS1zZXEgZGF0YSBmcm9tIHJldGluYWwgb3JnYW5vaWRzIGRlcml2ZWQgZnJvbQppbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbHMgKGlQU0NzKSB3aGljaCBpcyBpdHNlbGYgdGFrZSBmcm9tIGEgTENBIHBhdGllbnQgCndpdGggQ1JYLUkxMzhmcyBtdXRhdGlvbiBhbmQgdGhlaXIgaGVhbHRoeSBwYXJlbnQuIFRoaXMgbXV0YXRpb24gb2NjdXJzIGluIHRoZQp0cmFuc2FjdGl2YXRpb24gZG9tYWluLCBhbmQgc28gdGhlIGVmZmVjdCBvZiB0aGUgbXV0YXRpb24gaXN0IHRoYXQgQ1JYIGlzIG5vdAp0cmFuc2NyaWJlZC4gVGhlIGRhdGEgc291cmNlIGlzIEdTRTE1MjkzOS4gVGhlIGF1dGhvciBpcyB0cnlpbmcgdG8gc2hvdyB0aGF0IGEKZGVuby1hc3NvY2lhdGVkIHZpcnVzIGdlbmUgdGhlcmFweSBjYW4gcmVzdG9yZSBSTkEgZXhwcmVzc2lvbiBvZiBDUlguIEhvd2V2ZXIsCmFsdGhvdWdoIHRoaXMgc3R1ZHkgaXMgYWJvdXQgZ2VuZSB0aGVyYXB5LCB0aGUgZGF0YSB0aGF0IHNob3cgcmVzY3VlZCBleHByZXNzaW9uCmlzIGluIHNpbmdsZSBjZWxsIFJOQS1zZXEgZGF0YSwgd2hpY2ggaXMgbm90IGluIHRoZSBzY29wZSBvZiB0aGlzIHJlcG9ydCwgc28gaXQKd2FzIG5vdCBpbmNsdWRlZC4gVGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBmcm9tIDYgcmV0aW5hbCBvcmdhbm9pZHMsIGZvciA5MCBkYXlzLAoxMjUgZGF5cywgMTUwIGRheXMgYW5kIDIwMCBkYXkgYWZ0ZXIgdGhlIGlQU0NzIHN0YXJ0IHRvIGRpZmZlcmVudGlhdGU7IDMgb2YgdGhlCm9yZ2Fub2lkcyB3ZXJlIGZyb20gTENBIHBhdGllbnQsIGFuZCAzIG9yZ2Fub2lkcyB3ZXJlIGZyb20gdGhlaXIgaGVhbHRoeSBwYXJlbnQuCltAQ1JYXQoKTm90ZSB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIGNvZGUgaW4gdGhpcyByZXBvcnQgYXJlIHRha2UgZnJvbSBCQ0I0MjAgbGVjdHVyZQpbQGw0OyBAbDU7IEBsNjsgQGw3XSB3aXRoIG1pbm9yIG1vZGlmaWNhdGlvbiB0byBtYWtlIGl0IGFwcHJvcHJpYXRlIGZvciBteSBkYXRhLgoKTm90ZSAyOiBJIGNhbm5vdCBwdXQgYmlidGV4IGlubGluZSBjaXRhdGF0aW9uIHdpdGhpbiB0aGUgY29kZWNodW5rLCBzbyBpdCBpcyBsZWZ0CmF0IHRoZSBlbmQgb2YgdGhlIGNvZGUgY2h1bmssIGFuZCBzb21ldGltZXMgdGhlcmUgaXMgYSBmaWd1cmUsIHNvIHRoZSBmaWd1cmUKbGVnZW5kL2NhcHRpb24gbWF5IGJlIGRpcmVjdGx5IGZvbGxvd2VkIGJ5IHRoZSBpbmxpbmUgY2l0YXRpb24KCiMgQXNzaWdubWVudCAxIHByb2Nlc3NpbmcgCgpMZXRzIHVzIGZpcnN0IGRvIGFsbCB0aGUgcHJvY2Vzc2luZyB0aGF0IHdhcyBkb25lIGluIGFzc2lnbm1lbnQgMQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgiLi9hc3NpZ25tZW50X2Z1bmN0aW9ucy5yIikKc3VwcGxlbWVudGFyeUZpbGVzIDwtIGRvd25sb2FkU3VwcGxlbWVudGFyeUZpbGVzKCJHU0UxNTI5MzkiKQpDUlhfRXhwZXJpbWVudFJhd0NvdW50IDwtIHJlYWRGaWxlKHN1cHBsZW1lbnRhcnlGaWxlcykKQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkIDwtIGZpbHRlck91dExvd0NvdW50KENSWF9FeHBlcmltZW50UmF3Q291bnQpCm5vcm1hbGl6ZWRfY291bnRzIDwtIG5vcm1hbGl6ZVRoZUNvdW50cyhDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQpCmBgYApbQGw0OyBAbDU7IEBnZW9xdWVyeTsgQGJpb21hcnQ7IEBlZGdlcl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmVuc2VtYmxEYXRhU2V0IDwtIGdldEVuc2VtYmxiaW9tYXJ0KCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3QgPC0gbWFwVGhlRGF0YShDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQsIG5vcm1hbGl6ZWRfY291bnRzLCBlbnNlbWJsRGF0YVNldCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzYXZlUkRTKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90LCBmaWxlID0gIm5vcm1hbGl6ZWRfY291bnRzX2Fubm90LnJkcyIpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQXBwcmVudGx5IHRoZXJlIGlzIGEgZHVwbGlhdGUgZW1zZWJsZW0gaWQKbm9ybWFsaXplZF9jb3VudF9kYXRhIDwtIG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyFkdXBsaWNhdGVkKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyAsIGMoImVuc2VtYmxfZ2VuZV9pZCIpXSksXQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAxYTogVGhlIGR1cGxpY2F0ZSBnZW5lIHRoYXQgcmVtb3ZlZC4nfQpub3JtYWxpemVkX2NvdW50c19hbm5vdFtkdXBsaWNhdGVkKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyAsIGMoImVuc2VtYmxfZ2VuZV9pZCIpXSksXQoKYGBgClRhYmxlIDFhOiBUaGUgZHVwbGljYXRlIGdlbmUgdGhhdCB3YXMgcmVtb3ZlZC4KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAxYjogc2hvd2luZyB0aGUgdHdvIGdlbmUgaGFzIHRoZSBzYW1lIGVuc2VtYmwgZ2VuZSBpZCwgYnV0IG5vdCBleGFjdGx5IHRoZSBzYW1lIGhnbmMgc3ltYm9sJ30Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3Rbd2hpY2gobm9ybWFsaXplZF9jb3VudHNfYW5ub3QkZW5zZW1ibF9nZW5lX2lkID09ICdFTlNHMDAwMDAyNTQ4NzYnKSwgXQpgYGAKVGFibGUgMWI6IHNob3dpbmcgdGhlIHR3byBnZW5lIGhhcyB0aGUgc2FtZSBlbnNlbWJsIGdlbmUgaWQsIGJ1dCBub3QgZXhhY3RseSB0aGUgc2FtZSBoZ25jIHN5bWJvbAoKSSBmaW5kIHRoZSBvbmUgdGhhdCBpcyBjb25zaWRlciBhcyBkdXBsaWNhdGUsIHRoZW4gZmluZCB0aGUgb25lIHRoYXQgaGFzIHRoZSBzYW1lIGVubXNlbGJsZSBnZW5lIGlkIGFzIGl0LiAKRm9yIHNvbWUgcmVhc29uIHRoaXMgZW5zZW1ibCBnZW5lIGlkIG1hcHMgdG8gMiBoZ25jIHN5bWJvbHMsIFNVR1QxUDQtU1RSQTZMUCBhbmQgU1RSQTZMUCwgZXZlbiBmcm9tIHRoZSBuYW1lIGl0IGxvb2sgbGlrZSB0aGUgc2FtZSBnZW5lCgoKVGhpcyBnZW5lIHdhcyBoYXMgYSBleHByZXNzaW9uIG9mIDEwIHRpbWVzIGdyZWF0ZXIgdGhhbiB0aGUgMm5kIGhpZ2hlc3QgZXhwcmVzc2VkCmdlbmUsIGl0IHNlZXNtIGxpa2UgaXQgbWF5IGJlIHNvbWUgZXJyb3IsIHNvIEkgcmVtb3ZlZCBpdC4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpub3JtYWxpemVkX2NvdW50X2NsZWFuZWQgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhW25vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWQgIT0gJ0VOU0cwMDAwMDE1NjUwOCcsIF0KYGBgCgoKCiMgSGVhdG1hcCBmb3IgYWxsIGdlbmVzCgpmaXJzdCBtYWtlIGEgaGVhdCBtYXAgbWF0cml4ICh0aGUgaW5wdXQgdGhpbmcgdXNlIHRvIGdlbmVyYXRlIGEgaGVhdCBtYXApCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAyOiBUaGUgZmlyc3QgMTAwMCByb3cgb2YgdGhlIGZpbHRlcmVkIG5vcm1hbGl6ZWQgZGF0YSBmb3IgYWxsIHNhbXBsZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCcsIHdhcm5pbmc9RkFMU0V9CgpoZWF0bWFwX21hdHJpeCA8LSBub3JtYWxpemVkX2NvdW50X2NsZWFuZWRbLAogICAgICAgICAgICAgICAgICAgICAgICAzOm5jb2wobm9ybWFsaXplZF9jb3VudF9jbGVhbmVkKV0Kcm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZCRlbnNlbWJsX2dlbmVfaWQKY29sbmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZFssIDM6bmNvbChub3JtYWxpemVkX2NvdW50X2NsZWFuZWQpXSkKaGVhdG1hcF9tYXRyaXgKc2F2ZVJEUyhoZWF0bWFwX21hdHJpeCwgZmlsZSA9ICJoZWF0bWFwX2xpc3QucmRzIikKYGBgClRhYmxlIDI6IFRoZSBmaXJzdCAxMDAwIHJvdyBvZiB0aGUgZmlsdGVyZWQgbm9ybWFsaXplZCBkYXRhIGZvciBhbGwgc2FtcGxlcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkCgpbQGw2XQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaGVhdG1hcF9tYXRyaXggPC0gcmVhZFJEUygiLi9oZWF0bWFwX2xpc3QucmRzIikKYGBgCgoKdGhlbiBnZW5lcmF0ZSBhIGhlYXQgbWFwCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTogSW5pdGlhbCBoZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlciBieSBlbnNlbWJsIGlkLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmlmKG1pbihoZWF0bWFwX21hdHJpeCkgPT0gMCl7CiAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoICJ3aGl0ZSIsICJyZWQiKSkKCn0gZWxzZSB7CiAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4KSwgMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4WzE6MTAwMCxdKSwKICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICBjb2w9aGVhdG1hcF9jb2wsIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgIHJhc3Rlcl9yZXNpemVfbWF0ID0gVFJVRSwgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkluaXRpYWwgSGVhdG1hcCIpKQpgYGAKRmlndXJlIDE6IEluaXRpYWwgaGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXIgYnkgZW5zZW1ibCBpZC4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNjsgQGhlYXRtYXA7IEBjb2xvcl0KCgoKTm93IHdpdGggcm93IG5vcm1hbGl6YXRpb24gKGkuZS4gdGhlIGtpbmQgd2l0aCBzdWJ0cmFjdCB0aGUgbWVhbiBkaXZpZGVkIGJ5IHN0YW5kYXJkIGRldmlhdGlvbikKd2UgY2FuIHNlZSBlYWNoIGdlbmUgYmV0dGVyCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24uIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KaGVhdG1hcF9tYXRyaXggPC0gdChzY2FsZSh0KGhlYXRtYXBfbWF0cml4KSkpCgppZihtaW4oaGVhdG1hcF9tYXRyaXgpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYygwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXgpLCAwLCBtYXgoaGVhdG1hcF9tYXRyaXgpKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhbMToxMDAwLF0pLAogICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSxzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgY29sPWhlYXRtYXBfY29sLHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSxzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIiBIZWF0bWFwIHdpdGggcm93IG5vcm1hbGl6YXRpb24iKSkKYGBgCkZpZ3VyZSAyOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbi4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNjsgQGhlYXRtYXA7IEBjb2xvcl0KCnlvdSBjYW4gc2VlIHRoYXQgdGhlIHNhbXBsZXMgYXJlIG1vcmUgc2ltaWxhciB0byBlYWNoIG90aGVyIGF0IHRoZSBzYW1lIHRpbWUgcG9pbnQgdGhhbiB3aGV0aGVyIG9yIG5vdCBpdCBpcyBjb250cm9sLCBJIHRoaW5rIHRoYXQgaXMgcmVhc29uYWJsZSBzaW5jZSBtYW55IGdlbmUgZ2V0IHNpbXVsYXRlZCBhbmQgcmVwcmVzc2VkIGR1cmluZyBkZXZlbG9wbWVudCwgc28gdGhlIGdlbmVzIHRoYXQgYXJlIG9uIGlzIGRpZmZlcmVudCBmcm9tIG9uZSBkZXZlbG9wbWVudCB0aW1lIHBvaW50LCB0byBhbm90aGVyLiAKCiMgSGVhdG1hcCBmb3IgdG9waGl0cyB1c2luZyBMaW1tYQoKV2Ugd2lsbCBiZSBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBhIGxpbmVhciBtb2RlbCBhbmQgdXNpbmcgZW1waXJpY2FsIGJheWVzIHRvIApjYWxjdWxhdGUgcC12YWx1ZXMuIFRoZW4gd2Ugd2lsbCB1c2luZyBCZW5qYW1uaS1Ib2NoYmVyZyBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcwp0ZXN0aW5nLCBmb3IgY29ycmVjdGluZyBvdXIgcC12YWx1ZXMuCgojIyBTaW1wbGUgTW9kZWwKClRoZSBzaW1wbGUgbW9kZWwgaXMgd2hlcmUgdGhlIG1vZGVsIGFjY291bnRzIGZvciB0aGUgY29uZGl0aW9uIChjb250cm9sIHZzIApleHBlcmltZW50KSwgYW5kIHRoZSBkYXkgdGhhdCB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIG9uLCBidXQgbm90IGFjY291bnQgCmZvciB3aGljaCBvcmdhbm9pZHMgaXMgdGhlIGRhdGEgY29sbGVjdGVkIGZyb20uIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgMzogU2hvd2luZyBmb3IgZWNhaCBzYW1wbGUsIHdoaWNoIG9yZ2Fub2lkLCBhdCB3aGF0IHRpbWUsIGFuZCB3aGF0IGNvbmRpdGlvbiBpdCBpcyd9CnNhbXBsZXMgPC0gZGF0YS5mcmFtZShsYXBwbHkoY29sbmFtZXMoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKVszOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0sIEZVTj1mdW5jdGlvbih4KXt1bmxpc3Qoc3Ryc3BsaXQoeCwgc3BsaXQgPSAiXFxfIikpW2MoMSwgMiwgMyldfSkpCnNhbXBsZXMKY29sbmFtZXMoc2FtcGxlcykgPC0gY29sbmFtZXMoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKVszOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0Kcm93bmFtZXMoc2FtcGxlcykgPC0gYygiY29uZGl0aW9uIiwgInRpbWUiLCJwYXRpZW50IikKc2FtcGxlcyA8LSBkYXRhLmZyYW1lKHQoc2FtcGxlcykpCgptb2RlbF9kZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gc2FtcGxlcyRjb25kaXRpb24rc2FtcGxlcyR0aW1lKQoKZXhwcmVzc2lvbk1hdHJpeCA8LSBhcy5tYXRyaXgobm9ybWFsaXplZF9jb3VudF9kYXRhWywzOm5jb2woQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkKV0pCnJvd25hbWVzKGV4cHJlc3Npb25NYXRyaXgpIDwtCiAgbm9ybWFsaXplZF9jb3VudF9kYXRhJGVuc2VtYmxfZ2VuZV9pZApjb2xuYW1lcyhleHByZXNzaW9uTWF0cml4KSA8LQogIGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YSlbMzpuY29sKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZCldCm1pbmltYWxTZXQgPC0gQmlvYmFzZTo6RXhwcmVzc2lvblNldChhc3NheURhdGE9ZXhwcmVzc2lvbk1hdHJpeCkKCmZpdCA8LSBsaW1tYTo6bG1GaXQobWluaW1hbFNldCwgbW9kZWxfZGVzaWduKQpmaXQyIDwtIGxpbW1hOjplQmF5ZXMoZml0LHRyZW5kPVRSVUUpCgp0b3BmaXQgPC0gbGltbWE6OnRvcFRhYmxlKGZpdDIsCiAgICAgICAgICAgICAgICAgICBjb2VmPW5jb2wobW9kZWxfZGVzaWduKSwKICAgICAgICAgICAgICAgICAgIGFkanVzdC5tZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgbnVtYmVyID0gbnJvdyhleHByZXNzaW9uTWF0cml4KSkKI21lcmdlIGhnbmMgbmFtZXMgdG8gdG9wZml0IHRhYmxlCm91dHB1dF9oaXRzIDwtIG1lcmdlKG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMToyXSwKICAgICAgICAgICAgICAgICAgICAgdG9wZml0LAogICAgICAgICAgICAgICAgICAgICBieS55PTAsYnkueD0xLAogICAgICAgICAgICAgICAgICAgICBhbGwueT1UUlVFKQojc29ydCBieSBwdmFsdWUKb3V0cHV0X2hpdHMgPC0gb3V0cHV0X2hpdHNbb3JkZXIob3V0cHV0X2hpdHMkUC5WYWx1ZSksXQpgYGAKVGFibGUgMzogU2hvd2luZyBmb3IgZWNhaCBzYW1wbGUsIHdoaWNoIG9yZ2Fub2lkLCBhdCB3aGF0IHRpbWUsIGFuZCB3aGF0IGNvbmRpdGlvbiBpdCBpcwoKW0BsNjsgQGJpb2Jhc2U7IEBsaW1tYV0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSA0OiBTaG93IHRoZSBsaW1tYSBvdXRwdXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgc2ltcGxlIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwIHZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIGFkanVzdGVkIHAgdmFsdWVzIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAnfQprbml0cjo6a2FibGUob3V0cHV0X2hpdHNbMToxMCwxOjddLHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKVGFibGUgNDogU2hvdyB0aGUgbGltbWEgb3V0cHV0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIHNpbXBsZSBtb2RlbCwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUsIGFuZCBjb3JyZWN0aW5nIHRoZSBwLXZhbHVlLiBUaGlzIGlzIG9ubHkgc2hvd2luZyB0aGUgZmlyc3QgMTAsIG9yZGVyZWQgYnkgcCB2YWx1ZS4gTm90ZSB0aGF0IHRoZSBwIHZhbHVlIGFuZCBhZGp1c3RlZCBwIHZhbHVlcyBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwCgpbQGw2OyBAa25pdHJdCgpUaGlzIGlzIGhvdyBtYW55IGdlbmVzIHRoYXQgaGF2ZSBwdmFsdWUgdW5kZXIgMC4wNQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzJFAuVmFsdWUgPCAwLjA1KSkKYGBgCltAbDZdCgoKVGhpcyBpcyBob3cgbWFueSBnZW5lcyB0aGF0IHN0aWxsIHB2YWx1ZSBvZiBsZXNzIHRoYW4gMC4wNSBhZnRlciBhZGp1c3RtZW50CmBgYHtyIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0cyRhZGouUC5WYWwgPCAwLjA1KSkKYGBgCltAbDZdCgoKIyMgT3JnYW5vaWQgTW9kZWwKClRoZSBvcmdhbm9pZCBtb2RlbCBpcyB3aGVyZSB0aGUgbW9kZWwgYWNjb3VudHMgZm9yIHRoZSBjb25kaXRpb24gKGNvbnRyb2wgdnMgCmV4cGVyaW1lbnQpLCBhbmQgdGhlIGRheSB0aGF0IHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgb24sIGFuZCAKZm9yIHdoaWNoIG9yZ2Fub2lkcyBpcyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbS4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbF9kZXNpZ25fb3JnIDwtIG1vZGVsLm1hdHJpeCgKICB+IHNhbXBsZXMkcGF0aWVudCArIHNhbXBsZXMkY29uZGl0aW9uICsgc2FtcGxlcyR0aW1lKQpmaXRfcGF0IDwtIGxpbW1hOjpsbUZpdChtaW5pbWFsU2V0LCBtb2RlbF9kZXNpZ25fb3JnKQpgYGAKW0BsNjsgQGtuaXRyXQoKQ2hhbmdlIHRoZSBtb2RlbCwgZml0IHRoZSBkYXRhIHRvIHRoZSBtb2RlbCwgdGhlbiBhcHBseSBlbXBpcmNhbCBiYXllcyAsIAp0aGVuIGNvcnJlY3Qgd2l0aCBCSAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZml0X29yZyA8LSBsaW1tYTo6bG1GaXQobWluaW1hbFNldCwgbW9kZWxfZGVzaWduX29yZykKZml0Ml9vcmcgPC0gbGltbWE6OmVCYXllcyhmaXRfb3JnLHRyZW5kPVRSVUUpCgp0b3BmaXRfb3JnIDwtIGxpbW1hOjp0b3BUYWJsZShmaXQyX29yZywKICAgICAgICAgICAgICAgICAgIGNvZWY9bmNvbChtb2RlbF9kZXNpZ25fb3JnKSwKICAgICAgICAgICAgICAgICAgIGFkanVzdC5tZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgbnVtYmVyID0gbnJvdyhleHByZXNzaW9uTWF0cml4KSkKCiNtZXJnZSBoZ25jIG5hbWVzIHRvIHRvcGZpdCB0YWJsZQpvdXRwdXRfaGl0c19vcmcgPC0gbWVyZ2Uobm9ybWFsaXplZF9jb3VudF9kYXRhWywxOjJdLAogICAgICAgICAgICAgICAgICAgICAgICAgdG9wZml0X29yZywgYnkueT0wLCBieS54PTEsIGFsbC55PVRSVUUpCiNzb3J0IGJ5IHB2YWx1ZQpvdXRwdXRfaGl0c19vcmcgPC0gb3V0cHV0X2hpdHNfb3JnW29yZGVyKG91dHB1dF9oaXRzX29yZyRQLlZhbHVlKSxdCgpgYGAKW0BsNjsgQGxpbW1hXQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J1RhYmxlIDU6IFNob3cgdGhlIGxpbW1hIG91dHB1dCBhZnRlciBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBvcmdhbm9pZCBtb2RlbCwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUsIGFuZCBjb3JyZWN0aW5nIHRoZSBwLXZhbHVlLiBUaGlzIGlzIG9ubHkgc2hvd2luZyB0aGUgZmlyc3QgMTAsIG9yZGVyZWQgYnkgcC12YWx1ZS4gTm90ZSB0aGF0IHRoZSBwIHZhbHVlIGFuZCBhZGp1c3RlZCBwIHZhbHVlcyBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwJ30Ka25pdHI6OmthYmxlKG91dHB1dF9oaXRzX29yZ1sxOjEwLDE6N10sdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCmBgYApUYWJsZSA1OiBTaG93IHRoZSBsaW1tYSBvdXRwdXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgb3JnYW5vaWQgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAtdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgYWRqdXN0ZWQgcCB2YWx1ZXMgYXJlIG5vdCBhY3R1YWxseSAwLCB0aGV5IGFyZSBqdXN0IHJvdW5kZWQgdG8gMAoKW0BsNjsgQGtuaXRyXQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0c19vcmckUC5WYWx1ZSA8IDAuMDUpKQpgYGAKW0BsNl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0c19vcmckYWRqLlAuVmFsIDwgMC4wNSkpCmBgYApbQGw2XQoKIyMgQ29tcGFyaW5nIGJvdGggbW9kZWxzCgpUaGVyZSBpcyBhY3R1YWxseSBsZXNzLCBidXQgbGV0IHVzIGxvb2sgYXQgaXQgZnJvbSBhIGdyYXBoCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMzogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwIHRvIDAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrJ30Kc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWQsCiAgc2ltcGxlX3B2YWx1ZT1vdXRwdXRfaGl0cyRhZGouUC5WYWwpCgpvcmdfbW9kZWxfcHZhbHVlcyA8LSAgZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICBvdXRwdXRfaGl0c19vcmckZW5zZW1ibF9nZW5lX2lkLAogIG9yZ2Fub2lkX3B2YWx1ZSA9IG91dHB1dF9oaXRzX29yZyRhZGouUC5WYWwpCgp0d29fbW9kZWxzX3B2YWx1ZXMgPC0gbWVyZ2Uoc2ltcGxlX21vZGVsX3B2YWx1ZXMsCiAgb3JnX21vZGVsX3B2YWx1ZXMsYnkueD0xLGJ5Lnk9MSkKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImJsYWNrIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1XSA8LSAib3JhbmdlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJibHVlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1ICYKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gIlNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9Ik9yZ2Fub2lkIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICBtYWluPSJTaW1wbGUgdnMgT3JnYW5vaWQgTGltbWEiLAogICAgIHhsaW0gPSBjKDAsIDAuMSksCiAgICAgeWxpbSA9IGMoMCwgMC4xKSkKYGBgCkZpZ3VyZSAzOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgYW5kIHRoZSBvcmdhbm9pZCBtb2RlbCBpbiBsaW1tYSwgem9vbWVkIGluIG9uIDAgdG8gMC4xLiBIaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgZ2VuZXMgaW4gYm90aCBtb2RlbCBhcyByZWQsIGluIHNpbXBsZSBtb2RlbCBvbmx5IGFzIG9yYW5nZSwgaW4gb3JnYW5vaWQgbW9kZWwgb25seSBhcyByZWQsIGFuZCBub24tc2lnbmlmaWNhbnQgZ2VuZXMgYXMgYmxhY2sKW0BsNl0KClRvIGRldGVybWluZSB3aGljaCBtb2RlbCBpcyBtb3JlIGFwcHJvcHJpYXRlLCB3ZSB0YWtlIGEgbG9vayBhdCBwLXZhbHVlIG9mIHRoZQpnZW5lIG9mIGludGVyZXN0LCBDUlguIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDQ6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMCB0byAwLjEuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlggYXMgcmVkJ30KZW5zZW1ibF9vZl9pbnRlcmVzdCA8LSBub3JtYWxpemVkX2NvdW50X2RhdGEkZW5zZW1ibF9nZW5lX2lkWwogIHdoaWNoKG5vcm1hbGl6ZWRfY291bnRfZGF0YSRoZ25jX3N5bWJvbCA9PSAiQ1JYIildCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImdyZXkiCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQ9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJzaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJvcmdhbm9pZCBtb2RlbCBhZGp1c3RlZCAgcC12YWx1ZXMiLAogICAgIG1haW49IlNpbXBsZSB2cyBPcmdhbm9pZCBMaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMC4xKSwKICAgICB5bGltID0gYygwLCAwLjEpKQpwb2ludHModHdvX21vZGVsc19wdmFsdWVzW3doaWNoKAogIHR3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkID09IGVuc2VtYmxfb2ZfaW50ZXJlc3QpLDI6M10sCiAgICAgICBwY2g9MjAsIGNvbD0icmVkIiwgY2V4PTEuNSkKbGVnZW5kKDAsMSxsZWdlbmQ9YygiQ1JYIiwicmVzdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmV5IiksY2V4ID0gMC43KQpgYGAKRmlndXJlIDQ6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMCB0byAwLjEuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlggYXMgcmVkCltAbDZdCgpUaGUgc2ltcGxlIG1vZGVsIGlzIGJldHRlciwgSSBiZWxpZXZlIHRoYXQgaXMgdGhlIGJlY2F1c2UgdGhlcmUgYXJlIG1pbmltYWwgCmRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmV0aW5hbCBvcmdhbm9pZHMsIHNpbmNlIHRoZXkgYXJlIGRlcml2ZWQgZnJvbSBpUFNDIAp0aGF0IGlzIGZyb20gdGhlIHNhbWUgcGF0aWVudC4gCgoKIyMgSGVhdG1hcCBmcm9tIGxpbW1hIAoKTGV0IHVzIGxvb2sgYXQgdGhlIGhlYXRtYXAgb2Ygb25seSB0aGUgdG9wIGhpdHMgKGJ5IGNvcnJlY3RlZCBwdmFsdWUpLAppbiB0aGUgc2ltcGxlIG1vZGVsCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgNTogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIGJ5IGNvcnJlY3RlZCBwLXZhbHVlIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkWwogIG91dHB1dF9oaXRzJGFkai5QLlZhbDwwLjA1XQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbCndoaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KSAlaW4lIHRvcF9oaXRzKSxdKSkpCgoKaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogIH0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbMToxMDAwLF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgCiAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkhlYXRtYXAgZnJvbSBMaW1tYSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpgYGAKRmlndXJlIDU6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyBieSBjb3JyZWN0ZWQgcC12YWx1ZSB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGxpbW1hLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2XQoKCkxldCB1cyBub3QgY2x1c3RlciB0aGUgZ2VuZXMgYnV0IGdyb3VwIGNvbnRyb2xzIGFuZCBleHBlcmltZW50cyB0b2dldGhlcgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDY6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyBiYXNlZCBvbiBjb3JyZWN0ZWQgcC12YWx1ZSB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGxpbW1hLCB3aXRob3V0IGNsdXN0ZXJpbmcsIGFuZCBncm91cCBjb250cm9sIGFuZCBleHBlcmltZW50IHRvZ2V0aGVyLCByZXNwZWN0aXZlbHkuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkWwogIG91dHB1dF9oaXRzJGFkai5QLlZhbDwwLjA1XQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbCndoaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KSAlaW4lIHRvcF9oaXRzKSxdKSkpCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbLApjKAogIGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIHBhdHRlcm4gPSAiXFxDUlhMQ0EiKSwKICBncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCBwYXR0ZXJuID0gIlxcQ29udHJvbCIpCiApCl0KCmlmKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgICAgICAgICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkxpbW1hIEhlYXRtYXAgd2l0aG91dCBjbHVzdGVyaW5nIikpCmBgYApGaWd1cmUgNjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIGJhc2VkIG9uIGNvcnJlY3RlZCBwLXZhbHVlIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNl0KCldlIGNhbiBjbGVhcmx5IHRlbGwgdGhhdCB0aGUgdGVzdCBjb25kaXRpb24gZG8gbm90IGxvb2sgbGlrZSBlYWNoIG90aGVyLgpUaGUgY29udHJvbHMgYWxzbyBkbyBub3QgbG9vayBsaWtlIGVhY2ggb3RoZXIuCgoKVGhlbiBhZ2FpbiBsb29rIGF0IHRvcCBoaXRzIHdpdGggbGVzcyB0aGFuIDAuMDEgcHZhbHVlCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA3OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3IgaGl0cyB3aXRoIGNvcnJlY3RlZCBwLXZhbHVlIHNtYWxsZXIgdGhhbiAwLjAxIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gbGltbWEsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMnfQp0b3BfaGl0cyA8LSBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWRbCiAgb3V0cHV0X2hpdHMkYWRqLlAuVmFsIDwgMC4wMV0KCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKc2NhbGUodChoZWF0bWFwX21hdHJpeFt3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkgJWluJSB0b3BfaGl0cyksXSkpKQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSBoZWF0bWFwX21hdHJpeF90b3BoaXRzWywKICAgICAgIGMoZ3JlcChjb2xuYW1lcyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKSxwYXR0ZXJuID0gIlxcQ1JYTENBIiksCiAgICAgICAgIGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENvbnRyb2wiKSldCgpucm93KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpCgppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKCAwLCBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0c1sxOjEwMDAsXSksCiAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSxzaG93X2NvbHVtbl9kZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICBjb2w9aGVhdG1hcF9jb2wsc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSxzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkxpbW1hIEhlYXRtYXAsIG5vIGNsdXN0ZXIsIDAuMSIpKQpgYGAKRmlndXJlIDc6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBoaXRzIHdpdGggY29ycmVjdGVkIHAtdmFsdWUgc21hbGxlciB0aGFuIDAuMDEgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYSwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKCiMgSGVhdG1hcCB3aXRoIHRvcGhpdHMgZnJvbSBlZGdlUgoKIyMgU2ltcGxlIE1vZGVsCgpUaGUgc2ltcGxlIG1vZGVsIGlzIHdoZXJlIHRoZSBtb2RlbCBhY2NvdW50cyBmb3IgdGhlIGNvbmRpdGlvbiAoY29udHJvbCB2cyAKZXhwZXJpbWVudCksIGFuZCB0aGUgZGF5IHRoYXQgdGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBvbiwgYnV0IG5vdCBhY2NvdW50IApmb3Igd2hpY2ggb3JnYW5vaWRzIGlzIHRoZSBkYXRhIGNvbGxlY3RlZCBmcm9tLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpbHRlcmVkX2RhdGFfbWF0cml4IDwtIGFzLm1hdHJpeChDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWRbLDM6MjhdKQpyb3duYW1lcyhmaWx0ZXJlZF9kYXRhX21hdHJpeCkgPC0gQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkJGVucy5pZApkIDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cz1maWx0ZXJlZF9kYXRhX21hdHJpeCwgZ3JvdXA9c2FtcGxlcyRjb25kaXRpb24pCmQgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ24pCmZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZCwgbW9kZWxfZGVzaWduKQoKYGBgCltAbDY7IEBlZGdlcl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J1RhYmxlIDY6IFNob3cgdGhlIG91dHB1dCBvZiBnbG1RTEZpdCBhZnRlciBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBzaW1wbGUgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAtdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgRkRSIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAnfQpxbGYucG9zX3ZzX25lZyA8LSBlZGdlUjo6Z2xtUUxGVGVzdChmaXQsIGNvZWY9J3NhbXBsZXMkY29uZGl0aW9uQ1JYTENBJykKa25pdHI6OmthYmxlKGVkZ2VSOjp0b3BUYWdzKHFsZi5wb3NfdnNfbmVnKSwgdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCltAbDY7IEBlZGdlcjsgQGtuaXRyXQoKRXZlbiB0aG91Z2ggdGhpcyBzYXkgRkRSLCBidXQgc2luY2Ugd2UgYXJlIHVzaW5nIHRoZSBzYW1lIG11bHRpcGxlIGh5cG90aGVzaXMgCnRlc3RpbmcgbWV0aG9kIGZvciBib3RoIGVkZ2VSIGFuZCBsaW1tYSwgdGhleSBhcmUgdGhlIHNhbWUgdHlwZSBvZiBjb3JyZWN0ZWQKdmFsdWVzLiBUaGUgZmlyc3Qgb25lIGlzIGdlbmUgYmVmb3JlIGNvcnJlY3Rpb24sIGFuZCBzZWNvbmQgb25lIGlzIGFmdGVyIGNvcnJlY3Rpb24KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWxmX291dHB1dF9oaXRzIDwtIGVkZ2VSOjp0b3BUYWdzKHFsZi5wb3NfdnNfbmVnLHNvcnQuYnkgPSAiUFZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG5yb3cobm9ybWFsaXplZF9jb3VudF9kYXRhKSkKbGVuZ3RoKHdoaWNoKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRQVmFsdWUgPCAwLjA1KSkKbGVuZ3RoKHdoaWNoKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRGRFIgPCAwLjA1KSkKYGBgCltAbDY7IEBlZGdlcl0KCiMjIE9yZ2Fub2lkIG1vZGVsCgpUaGUgb3JnYW5vaWQgTW9kZWwgaXMgd2hlcmUgdGhlIG1vZGVsIGFjY291bnRzIGZvciB0aGUgY29uZGl0aW9uIChjb250cm9sIHZzIApleHBlcmltZW50KSwgYW5kIHRoZSBkYXkgdGhhdCB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIG9uLCBhbmQgCmZvciB3aGljaCBvcmdhbm9pZHMgaXMgdGhlIGRhdGEgY29sbGVjdGVkIGZyb20uIAoKYGBge3Igd2FybmluZz1GQUxTRX0KZmlsdGVyZWRfZGF0YV9tYXRyaXggPC0gYXMubWF0cml4KENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZFssMzoyOF0pCnJvd25hbWVzKGZpbHRlcmVkX2RhdGFfbWF0cml4KSA8LSBDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQkZW5zLmlkCmQgPC0gZWRnZVI6OkRHRUxpc3QoY291bnRzPWZpbHRlcmVkX2RhdGFfbWF0cml4LCBncm91cD1zYW1wbGVzJGNvbmRpdGlvbikKZF9vcmcgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ25fb3JnKQpmaXRfb3JnIDwtIGVkZ2VSOjpnbG1RTEZpdChkX29yZywgbW9kZWxfZGVzaWduX29yZykKCmBgYApbQGw2OyBAZWRnZXJdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgNzogU2hvdyB0aGUgb3V0cHV0IG9mIGdsbVFMRml0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIG9yZ2Fub2lkIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwLXZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIEZEUiBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwJ30KcWxmLnBvc192c19uZWcgPC0gZWRnZVI6OmdsbVFMRlRlc3QoZml0X29yZywgY29lZj0nc2FtcGxlcyRjb25kaXRpb25DUlhMQ0EnKQprbml0cjo6a2FibGUoZWRnZVI6OnRvcFRhZ3MocWxmLnBvc192c19uZWcpLCB0eXBlPSJodG1sIixyb3cubmFtZXMgPSBGQUxTRSkKYGBgCgoKW0BsNjsgQGtuaXRyOyBAZWRnZXJdCgpUaGUgZmlyc3Qgb25lIGlzIGdlbmUgYmVmb3JlIGNvcnJlY3Rpb24sIGFuZCBzZWNvbmQgb25lIGlzIGFmdGVyIGNvcnJlY3Rpb24KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWxmX291dHB1dF9oaXRzX29yZyA8LSBlZGdlUjo6dG9wVGFncyhxbGYucG9zX3ZzX25lZyxzb3J0LmJ5ID0gIlBWYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRfZGF0YSkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlJFBWYWx1ZSA8IDAuMDUpKQpsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzX29yZyR0YWJsZSRGRFIgPCAwLjA1KSkKYGBgCltAbDY7IEBlZGdlcl0KCiMjIENvbXBhcmUgMiBtb2RlbCBmcm9tIGVkZ2VSCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgODogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrJ30Kc2ltcGxlX21vZGVsX3B2YWx1ZXNfcWxmIDwtIGRhdGEuZnJhbWUoZW5zZW1ibF9pZCA9CiAgcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICBzaW1wbGVfcHZhbHVlID0gcWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUikKCm9yZ19tb2RlbF9wdmFsdWVzX3FsZiA8LSAgZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlKSwKICBvcmdhbm9pZF9wdmFsdWUgPSBxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlJEZEUikKCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShzaW1wbGVfbW9kZWxfcHZhbHVlc19xbGYsCiAgb3JnX21vZGVsX3B2YWx1ZXNfcWxmLCBieS54PTEsIGJ5Lnk9MSkKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImJsYWNrIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1XSA8LSAib3JhbmdlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJibHVlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1ICYKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gIlNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9Ik9yZ2Fub2lkIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICBtYWluPSJTaW1wbGUgdnMgT3JnYW5vaWQgZWRnZVIiLAogICAgIHhsaW09YygwLCAwLjEpLAogICAgIHlsaW09YygwLCAwLjEpKQpgYGAKRmlndXJlIDg6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGdsbVFMRml0LCB6b29tZWQgaW4gb24gMC0wLjEuIEhpZ2hsaWdodGluZyBzaWduaWZpY2FudCBnZW5lcyBpbiBib3RoIG1vZGVsIGFzIHJlZCwgaW4gc2ltcGxlIG1vZGVsIG9ubHkgYXMgb3JhbmdlLCBpbiBvcmdhbm9pZCBtb2RlbCBvbmx5IGFzIHJlZCwgYW5kIG5vbi1zaWduaWZpY2FudCBnZW5lcyBhcyBibGFjawpbQGw2XQoKVGhlIG1ham9yaXR5IG9mIGdlbmVzIHRoYXQgaGF2ZSBsZXNzIHRoYW4gMC4wNSBwLXZhbHVlIGluIGVpdGhlciBtb2RlbCwgaGFzIGxlc3MgdGhhbiAwLjA1IApwLXZhbHVlIGluIGJvdGggbW9kZWwuCgpUbyBkZXRlcm1pbmUgd2hpY2ggbW9kZWwgaXMgbW9yZSBhcHByb3ByaWF0ZSwgd2UgdGFrZSBhIGxvb2sgYXQgcD12YWx1ZSBvZiB0aGUKZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA5OiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgYW5kIHRoZSBvcmdhbm9pZCBtb2RlbCBpbiBnbG1RTEZpdCwgem9vbWVkIGluIG9uIDAtMC4wMS4gSGlnaGxpZ2h0aW5nIG9ubHkgdGhlIGdlbmUgb2YgaW50ZXJlc3QsIENSWCd9CmVuc2VtYmxfb2ZfaW50ZXJlc3QgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhJGVuc2VtYmxfZ2VuZV9pZFsKICB3aGljaChub3JtYWxpemVkX2NvdW50X2RhdGEkaGduY19zeW1ib2wgPT0gIkNSWCIpXQp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJncmV5Igp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkPT0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuc2VtYmxfb2ZfaW50ZXJlc3RdIDwtICJyZWQiCnBsb3QodHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAic2ltcGxlIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0ib3JnYW5vaWQgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlNpbXBsZSB2cyBPcmdhbm9pZCBMaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMC4wMSksCiAgICAgeWxpbSA9IGMoMCwgMC4wMSkpCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbd2hpY2goCiAgdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdCksMjozXSwKICAgICAgIHBjaD0yMCwgY29sPSJyZWQiLCBjZXg9MS41KQpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJDUlgiLCJyZXN0IiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZXkiKSxjZXggPSAwLjcpCmBgYApGaWd1cmUgOTogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMDEuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlgKW0BsNl0KClRoZSBzaW1wbGUgbW9kZWwgaXMgYmV0dGVyLCBJIGJlbGlldmUgdGhpcyBpcyBzdGlsbCBkdWUgdG8gdGhlIHNhbWUgcmVhc29uIHRoYXQsCnRoZXJlIGFyZSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmV0aW5hbCBvcmdhbm9pZHMsIHNpbmNlIHRoZXkgYXJlIApkZXJpdmVkIGZyb20gaVBTQyB0aGF0IGlzIGZyb20gdGhlIHNhbWUgcGF0aWVudC4gCgoKIyMgQ29tcGFyZSBsaW1tYSB3aXRoIHF1YXNpLWxpa2VsaWhvb2QKCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDEwOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQgYW5kIGluIGxpbW1hLiBIaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgZ2VuZXMgaW4gYm90aCBtb2RlbCBhcyByZWQsIGluIHNpbXBsZSBtb2RlbCBvbmx5IGFzIG9yYW5nZSwgaW4gb3JnYW5vaWQgbW9kZWwgb25seSBhcyByZWQsIGFuZCBub24tc2lnbmlmaWNhbnQgZ2VuZXMgYXMgYmxhY2snfQpxbGZfc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgIGVuc2VtYmxfaWQgPSByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHMkdGFibGUpLAogICAgICAgICAgcWxmX3NpbXBsZV9wdmFsdWUgPSBxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSKQoKbGltbWFfc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgbGltbWFfc2ltcGxlX3B2YWx1ZSA9IG91dHB1dF9oaXRzJGFkai5QLlZhbCkKCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShxbGZfc2ltcGxlX21vZGVsX3B2YWx1ZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1tYV9zaW1wbGVfbW9kZWxfcHZhbHVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9MSxieS55PTEpCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJibGFjayIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJHFsZl9zaW1wbGVfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdIDwtICJvcmFuZ2UiCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9zaW1wbGVfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdIDwtICJibHVlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkcWxmX3NpbXBsZV9wdmFsdWUKICAgICAgICAgICAgICAgICAgICAgICAgICA8MC4wNSAmCnR3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9zaW1wbGVfcHZhbHVlPDAuMDVdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRxbGZfc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfc2ltcGxlX3B2YWx1ZSwKICAgICBjb2wgPSB0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyLAogICAgIHhsYWIgPSAiUUxGIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9IkxpbW1hIHNpbXBsZSBtb2RlbCBhZGp1c3RlZCBwLXZhbHVlcyIsCiAgICAgbWFpbj0iUUxGIHZzIExpbW1hIiwKICAgICB4bGltID0gYygwLCAxKSwKICAgICB5bGltID0gYygwLCAxKSkKYGBgCkZpZ3VyZSAxMDogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0IGFuZCBpbiBsaW1tYS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrCltAbDZdCgpGb3Igc29tZSByZWFzb24sIHRoaXMgaXMgYWxsIG92ZXIgdGhlIHBsYWNlCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTE6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCBhbmQgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwLTAuMDIuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlgnfQplbnNlbWJsX29mX2ludGVyZXN0IDwtIG5vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWRbCiAgd2hpY2gobm9ybWFsaXplZF9jb3VudF9kYXRhJGhnbmNfc3ltYm9sID09ICJDUlgiKV0KCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImdyZXkiCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkCiAgICAgICAgICAgICAgICAgICAgICAgICAgPT1lbnNlbWJsX29mX2ludGVyZXN0XSA8LSAicmVkIgoKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkcWxmX3NpbXBsZV9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJGxpbW1hX3NpbXBsZV9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gIlFMRiBzaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJMaW1tYSBzaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlFMRiB2cyBMaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMC4wMiksCiAgICAgeWxpbSA9IGMoMCwgMC4wMikpCgpwb2ludHModHdvX21vZGVsc19wdmFsdWVzWwogIHR3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkPT1lbnNlbWJsX29mX2ludGVyZXN0LDI6M10sCiAgICAgICBwY2g9MjQsICBjb2w9InJlZCIsIGNleD0xLjUpCmBgYApGaWd1cmUgMTE6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCBhbmQgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwLTAuMDIuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlgKW0BsNl0KCkl0IGxvb2tzIGxpa2UgUUxGIGlzIGJldHRlciB0aGFuIGxpbW1hLCBhbmQgdGhhdCBkb2VzIG1ha2Ugc2Vuc2UsIHNpbmNlIGxpbW1hCndhcyBtYWRlIGZvciBtaWNyb2FycmF5LCBidXQgSSBhbSB1c2luZyBidWxrIFJOQS1zZXEgZGF0YSwgd2hpY2ggZWRnZVIgbWV0aG9kcwphcmUgZGVzaWduZWQgZm9yLiAKCgojIyBIZWF0bWFwIGZyb20gZWRnZVIKCmhlYXRtYXAsIGJ1dCB1c2luZyB0b3BoaXRzIGZyb20gcWxmIGluc3RlYWQgb2YgbGltbWEgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAxMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKVsKICBxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSPDAuMDVdCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIHQoCiAgc2NhbGUodChoZWF0bWFwX21hdHJpeFt3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkKJWluJSB0b3BfaGl0cyksXSkpKQoKbnJvdyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKQoKaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCAid2hpdGUiLCAicmVkIikpCn0gZWxzZSB7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSwgMCwKICAgICAgICAgICAgICAgICAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9aGVhdG1hcF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsIAogICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJnbG1RTEZpdCBIZWF0bWFwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCkZpZ3VyZSAxMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzCltAbDY7IEBoZWF0bWFwOyBAY29sb3JdCgpjb21wYXJlIHRvIHRoZSBoZWF0bWFwIGZyb20gbGVtbWEsIHRoZSBkYXkgMjAwIGRvIG5vdCBjbHVzdGVyIHRvZ2V0aGVyLCAKYnV0IHRoZSBkYXkgMTUwIGFuZCBkYXkgMTI1IGNsdXN0ZXIgaW50byAyIGdyb3Vwcy4gSG93ZXZlciBhbGwgZGF5IDkwIGRvIGNsdXN0ZXIKdG9nZXRoZXIuIAoKdGhlbiBhbHNvIGNsdXN0ZXIgYnkgY29udHJvbCB2cyBleHBlcmltZW50CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTM6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0LCB3aXRob3V0IGNsdXN0ZXJpbmcsIGFuZCBncm91cCBjb250cm9sIGFuZCBleHBlcmltZW50IHRvZ2V0aGVyLCByZXNwZWN0aXZlbHkuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzJ30KdG9wX2hpdHMgPC0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKVtxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDVdCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKICBzY2FsZSh0KGhlYXRtYXBfbWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KQolaW4lIHRvcF9oaXRzKSxdKSkpCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzPC0gaGVhdG1hcF9tYXRyaXhfdG9waGl0c1ssIApjKGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENSWExDQSIpLAogIGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJcXENvbnRyb2wiKSldCgpucm93KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpCgppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKCAwLCBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQp9CgpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwgCiAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gImdsbVFMRml0IEhlYXRtYXAgbm8gY2x1c3RlciIpKQpgYGAKRmlndXJlIDEzOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKIyBPdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2lzCgoKVGhpcyBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIChJIGRpZCBjaGVjayB0aGUgbW9kZWwKZGVzaWduLCBjb250cm9sIHdhcyBsYWJlbGVkIGFzIDAgYW5kIGV4cGVyaW1lbnQvQ1JYIHdhcyBsYWJlbGVkIGFzIDEpLCBzbyAKdGhpcyBkb2VzIG1lYW4gdGhhdCB0aGF0IGlzIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGluIGV4cGVyaW1lbnQuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDID4gMCkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDIDwgMCkpCmBgYApbQGw3XQoKSSBvbmx5IG91dHB1dHRlZCB0aGUgZmlyc3QgNTAwMCBnZW5lcywgYmVjYXVzZSBnOnByb2ZpbGVyIHdpbGwgY3Jhc2ggd2l0aCB0b28gCm1hbnkgZ2VuZXMuIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnFsZl9vdXRwdXRfaGl0c193aXRoZ24gPC0gbWVyZ2UoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkWywxOjJdLCBxbGZfb3V0cHV0X2hpdHMsIGJ5Lng9MSwgYnkueSA9IDApCgpxbGZfb3V0cHV0X2hpdHNfd2l0aGduWywicmFuayJdIDwtIC1sb2cocWxmX291dHB1dF9oaXRzX3dpdGhnbiRGRFIsIGJhc2UgPTEwKSAqIHNpZ24ocWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQykKCnFsZl9vdXRwdXRfaGl0c193aXRoZ24gPC0gcWxmX291dHB1dF9oaXRzX3dpdGhnbltvcmRlcihxbGZfb3V0cHV0X2hpdHNfd2l0aGduJHJhbmspLF0KCnVwcmVndWxhdGVkX2dlbmVzIDwtIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kZ2VuZS5pZFsKICB3aGljaChxbGZfb3V0cHV0X2hpdHNfd2l0aGduJEZEUiA8IDAuMDUgCiAgICAgICAgICAgICAmIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kbG9nRkMgPiAwKV0KCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gcWxmX291dHB1dF9oaXRzX3dpdGhnbiRnZW5lLmlkWwogIHdoaWNoKHFsZl9vdXRwdXRfaGl0c193aXRoZ24kRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQyA8IDApXQoKZmlyc3Q1MDAwX2dlbmVzIDwtIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kZ2VuZS5pZFtjKDE6NTAwMCldCgppZiAoIWZpbGUuZXhpc3RzKCJkYXRhIikpIHsKIGRpci5jcmVhdGUoImRhdGEiKQp9Cgp3cml0ZS50YWJsZSh4PXVwcmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF91cHJlZ3VsYXRlZF9nZW5lcy50eHQiKSwgc2VwID0gIlx0IiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKd3JpdGUudGFibGUoeD1kb3ducmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF9kb3ducmVndWxhdGVkX2dlbmVzLnR4dCIpLHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCndyaXRlLnRhYmxlKHg9Zmlyc3Q1MDAwX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF9yYW5rZWRfZ2VuZWxpc3QudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCmBgYApbQGw3XQoKCiMgUmVzdWx0cyBvZiBHOnByb2ZpbGVyCgohW10oLi93aG9sZWxpc3Rfb3JkZXJlZC5wbmcpCgohW10oLi9kb3ducmVndWxhdGVkX29yZGVyZWQucG5nKQoKIVtdKC4vdXByZWd1bGF0ZWRfb3JkZXJlZC5wbmcpCgojIFZvbGNhbm8gUGxvdAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWxmX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgIGVuc2VtYmxfaWQgPSByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHMkdGFibGUpLAogICAgICAgICAgcWxmX3B2YWx1ZXMgPSAtbG9nMTAocWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUikpCgpxbGZfRkMgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICAgICAgICAgIHFsZl9GQyA9IHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRsb2dGQykKCnB2YWx1ZV9GQyA8LSBtZXJnZShxbGZfcHZhbHVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFsZl9GQywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9MSxieS55PTEpCgpwdmFsdWVfRkMkY29sb3VyIDwtICJibGFjayIKCnB2YWx1ZV9GQyRjb2xvdXJbd2hpY2gocHZhbHVlX0ZDJHFsZl9GQyA+IDApXSA8LSAiYmx1ZSIKCnB2YWx1ZV9GQyRjb2xvdXJbd2hpY2gocHZhbHVlX0ZDJHFsZl9GQyA8IDApXSA8LSAicmVkIgoKcGxvdChwdmFsdWVfRkMkcWxmX3B2YWx1ZXMsCiAgICAgcHZhbHVlX0ZDJHFsZl9GQywKICAgICBjb2wgPSBwdmFsdWVfRkMkY29sb3VyLAogICAgIHhsYWIgPSAiUUxGIC1sb2cxMCBhZGp1c3RlZCBwdmFsdWUiLAogICAgIHlsYWIgPSJRTEYgbG9nIGZvbGQgY2hhbmdlcyIsCiAgICAgbWFpbj0iVm9sY2FubyBwbG90IiwKICAgICB4bGltID0gYygwLCAxMCksCiAgICAgeWxpbSA9IGMoLTMsIDMpKQpgYGAKRmlndXJlIDE0OiB2b2xjYW5vIHBsb3Qgb2YgYWxsIGdlbmVzLCB6b29tZWQgaW4gdG8gLTIgdG8gMiBmb3IgbG9nRkMsIGFuZCAwIHRvCjEwIGZvciBGRFIgdG8gc2hvdyB0aGUgY2hhcmF0ZXJpc3RpYyBVIHNoYXBlLgoKVGhlcmUgaXMgdmlzdWFsIHNlcGFyYXRpb24gb2YgdXByZWd1bGF0ZWQgZ2VuZXMgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZSwgd2UgY2FuIApjbGVhcmx5IHNlZSB0aGUgCgpgYGB7ciBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9RkFMU0V9CgoKcWxmX3B2YWx1ZXNfaW50ZXJlc3QgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgIGVuc2VtYmxfaWQgPSByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHMkdGFibGUpLAogICAgICAgICAgcWxmX3B2YWx1ZXMgPSAtbG9nMTAocWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUikpCgpxbGZfRkNfaW50ZXJlc3QgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICAgICAgICAgIHFsZl9GQyA9IHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRsb2dGQykKCnB2YWx1ZV9GQ19pbnRlcmVzdCA8LSBtZXJnZShxbGZfcHZhbHVlc19pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFsZl9GQ19pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9MSxieS55PTEpCgpwdmFsdWVfRkNfaW50ZXJlc3QkY29sb3VyIDwtICJncmV5IgoKcHZhbHVlX0ZDX2ludGVyZXN0JGNvbG91cltwdmFsdWVfRkMkZW5zZW1ibF9pZD09ZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKCnBsb3QocHZhbHVlX0ZDX2ludGVyZXN0JHFsZl9wdmFsdWVzLAogICAgIHB2YWx1ZV9GQ19pbnRlcmVzdCRxbGZfRkMsCiAgICAgY29sID0gcHZhbHVlX0ZDX2ludGVyZXN0JGNvbG91ciwKICAgICB4bGFiID0gIlFMRiAtbG9nMTAgYWRqdXN0ZWQgcHZhbHVlIiwKICAgICB5bGFiID0iUUxGIGxvZyBmb2xkIGNoYW5nZXMiLAogICAgIG1haW49IlZvbGNhbm8gcGxvdCIsCiAgICAgeGxpbSA9IGMoMi41LCAzLjUpLAogICAgIHlsaW0gPSBjKC0xLCAxKSkKYGBgCkZpZ3VyZSAxNTogdm9sY2FubyBwbG90IG9mIGFsbCBnZW5lcywgem9vbWVkIGluIHRvIC0xIHRvIDEgZm9yIGxvZ0ZDLCBhbmQgMi41IHRvCjMuNSBmb3IgRkRSIHNvIHRoYXQgdGhlIGdlbmUgb2YgaW50ZXJlc3QgZG9lcyBub3QgZ2V0IGNvdmVyZWQgYnkgb3RoZXIgZ2VuZXMuCgoKIyBEaXNjdXNzaW9uIAoKV2UgaGF2ZSAzIGZhY3RvcnMgdGhhdCB3ZSBjYW4gaW5jbHVkZSBpbiBvdXIgbW9kZWwgZGVzaWduLiBUaGUgZGF5IHRoYXQgdGhlIGRheQp3YXMgY29sbGVjdGVkLCB3aGljaCBvcmdhbm9pZCB3YXMgaXQgY29sbGVjdGVkIGZyb20sIGlzIGl0IGV4cGVyaW1lbnQgb3IgY29udHJvbAp0aGF0IHdlIGFyZSBjb2xsZWN0aW5nIGZyb20/CgpXZSBkbyBoYXZlIHRvIGluY2x1ZGUgdGhlIGxhc3Qgb25lIChleHBlcmltZW50IHZzIGNvbnRyb2wpLCBvciBlbHNlIHdlIGFyZSBub3QKZ2V0dGluZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZm9yIHRoZSBxdWVzdGlvbiB0aGF0IHRoZSBhdXRob3JzIGlzIGFza2luZy4KW0BDUlhdLiBBbHRob3VnaCBhdCBmaXJzdCwgdGhlIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnRzIGRvIG5vdCBkaWZmZXIgdGhhdCBtdWNoLApidXQgYnkgZGF5IDIwMCB0aGV5IHRoZSBjb250cm9scyBjbHVzdGVyIHRvZ2V0aGVyIG1vcmUgdGhhbiB0aGUgb3JnYW5vaWRzLCAKc28gaXMgdGhlIGV4cGVyaW1lbnRzLiBJIGJlbGlldmUgd2UgY2FuIGNvbmNsdWRlIHRoYXQsIGJ5IGRheSAyMDAsIHRoZSBkaWZmZXJlbmNlCmZyb20gQ1JYLCBpcyBtb3JlIHRoYW4gdGhlIG9yZ2Fub2lkIHZhcmlhYmlsaXR5LiAKCkxvb2tpbmcgYXQgdGhlIE1EUyBwbG90IGZyb20gQTEsIHdlIGNhbiB0ZWxsIHRoYXQgdGhlIGRheSBpdCB3YXMgY29sbGVjdGVkIGZyb20KY2hhbmdlcyB0aGUgZGF0YSBieSBhIGxvdCwgc2luY2UgdGhlIFJOQSBpcyB0YWtlbiBmcm9tIHJldGluYWwgb3JnYW5vaWRzIGluIApkZXZlbG9wbWVudCwgYW5kIGdlbmUgZXhwcmVzc2lvbiBmb3IgY2VsbCBpbiBkZXZlbG9wbWVudCBjaGFuZ2VzIGZyb20gc3RhZ2UKdG8gc3RhZ2UuIFRoZXJlZm9yZSB3ZSBzaG91bGQgaW5jbHVkZSB0aGlzLgoKU3RpbGwgbG9vayBhdCB0aGUgTURTIHBsb3QgZnJvbSBBMSwgd2hpY2ggb3JnYW5vaWQgaXQgd2FzIGNvbGxlY3RlZCBmcm9tIGRvZXMKbm90IG1ha2UgbXVjaCBvZiBhIGRpZmZlcmVuY2UsIHRoaXMgaXMgcHJvYmFibHkgYmVjYXVzZSB0aGF0IGFsbCBjb250cm9sCm9yZ2Fub2lkcyBhcmUgZnJvbSB0aGUgc2FtZSBwZXJzb24sIGFuZCBhbGwgZXhwZXJpbWVudCBvcmdhbm9pZHMgYXJlIGZyb20gdGhlCnNhbWUgcGVyc29uLiAKCgojIFF1ZXN0aW9ucwoKMS4JQ2FsY3VsYXRlIHAtdmFsdWVzIGZvciBlYWNoIG9mIHRoZSBnZW5lcyBpbiB5b3VyIGV4cHJlc3Npb24gc2V0LiBIb3cgbWFueSBnZW5lcyB3ZXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkPyBXaGF0IHRocmVzaG9sZHMgZGlkIHlvdSB1c2UgYW5kIHdoeT8KCiAgICBJbiB0aGUgbW9kZWwgdGhhdCBkb2VzIG5vdCBhY2NvdW50IGZvciBvcmdhbm9pZCB2YXJpYWJpbGl0eSwgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IHdoZXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzcyB3YXMgODE3OC4gSW4gdGhlIG1vZGVsIHRoYXQgYWNjb3VudCBmb3Igb3JnYW5vaWQgdmFyaWFiaWxpdHksIHRoZSBudW1iZXIgaXMgNzgzOC4gRnJvbSBlZGdlUiwgd2UgZ290IDY1NTYgZ2VuZXMgdW5kZXIgdGhyZXNob2xkLiAKCiAgICBJIHVzZWQgdGhlIHRocmVzaG9sZCBvZiAwLjA1LCBiZWNhdXNlIG9mIGNvbnZlbnRpb24gYW5kIEkgZG8gbm90IGhhdmUgYWRkaXRpb25hbCBrbm93bGVkZ2UgKGZyb20gdGhlIG9yaWdpbmFsIHBhcGVyIG9yIGFueXdoZXJlKSBhYm91dCB3aGF0IHRocmVzaG9sZCB3b3VsZCBnaXZlIG1lIGJlc3QgcmVzdWx0cyBmb3IgYnVsayBSTkEgc2VxIGRhdGEgZnJvbSByZXRpbmFsIG9yZ2Fub2lkcyBkZXJpdmVkIGZyb20gaVBTQyB3aGVyZSB0aGUgdGVzdGluZyBjb25kaXRpb24gaXMgQ1JYLUxDQSBhbmQgdGhlIHNwZWNpZmljIG11dGF0aW9uIGlzIEkxMzhmczQ4LgoKMi4JTXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIC0gY29ycmVjdCB5b3VyIHAtdmFsdWVzIHVzaW5nIGEgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uIG1ldGhvZC4gV2hpY2ggbWV0aG9kIGRpZCB5b3UgdXNlPyBBbmQgV2h5PyBIb3cgbWFueSBnZW5lcyBwYXNzZWQgY29ycmVjdGlvbj8KCiAgICBJIHRyaWVkIHVzaW5nIEJlbmphbW5pIC0gSG9jaGJlcmcgZnJvbSBib3RoIGxpbW1hIGFuZCBlZGdlUiB0byBjb3JyZWN0IHRoZSBwLXZhbHVlIGZvciBteSBnZW5lcy4gVGhlIG9uZSB0aGF0IEkgd291bGQgZ28gd2l0aCBpcyB0aGUgQkggZnJvbSBlZGdlUiB3aXRoIGdsbVFMRlRlc3QgaW4gdGhlIHNpbXBsZSBtb2RlbCwgc2luY2UgdGhlIHNpbXBsZSBtb2RlbCBpcyBzbGlnaHRseSBiZXR0ZXIgYW5kIHRoZSByZXN1bHQgYXJlIHNpbWlsYXIgKGNvbXBhcmluZyBlZGdlUiBhbmQgbGltbWEpIGJ1dCBlZGdlUiBpcyBkZXNpZ25lZCBidWxrIFJOQS1zZXEgYW5kIGxpbW1hIGlzIG5vdC4gRm9yIG51bWJlciBvZiBnZW5lcyBwYXNzZWQgY29ycmVjdGlvbiwgbGltbWEgZ2F2ZSBtZSA2MTc0IGFuZCA1Njc3LCBmb3IgdGhlIDIgbW9kZWxzIHJlc3BlY3RpdmVseSwgZWRnZVIgZ2F2ZSBtZSA0MjIyLiAKSSB1c2VkIEJlbmphbW5pIC0gSG9jaGJlcmcsIGJlY2F1c2UgdGhhdCBzZWVtcyBsaWtlIGl0IGlzIHRoZSBtb3N0IHdpZGVseSB1c2VkLiAKCjMuCVNob3cgdGhlIGFtb3VudCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgdXNpbmcgYW4gTUEgUGxvdCBvciBhIFZvbGNhbm8gcGxvdC4gSGlnaGxpZ2h0IGdlbmVzIG9mIGludGVyZXN0LgoKICAgIHNlZSB2b2xjYW5vIHBsb3Qgc2VjdGlvbgoKNC4JVmlzdWFsaXplIHlvdXIgdG9wIGhpdHMgdXNpbmcgYSBoZWF0bWFwLiBEbyB5b3UgY29uZGl0aW9ucyBjbHVzdGVyIHRvZ2V0aGVyPyBFeHBsYWluIHdoeSBvciB3aHkgbm90LgoKICAgIE15IGNvbmRpdGlvbnMgZG8gbm90IGNsdXN0ZXIgdG9nZXRoZXIgaW4gdGhlIHNlbnNlIHRoYXQgdGhlIHRlc3QgY29uZGl0aW9ucyBhbmQgdGhlIGNvbnRyb2xzIGRvIG5vdC4gSG93ZXZlciwgdGhlIGRheXMgKGUuZy4gZXhwcmVzc2lvbiBvbiBkYXkgOTAgdnMgZXhwcmVzc2lvbiBvbiBkYXkgMjAwIHZzIGV4cHJlc3Npb24gb24gZGF5IDEyNS8xNTApLCBkbyBjbHVzdGVyIHRvZ2V0aGVyLCBJIGJlbGlldmUgdGhpcyBpcyBiZWNhdXNlIGR1cmluZyBkZXZlbG9wbWVudCBtYW55IGdlbmVzIGdldCBleHByZXNzZWQgYW5kIHJlcHJlc3NlZCwgc28gdGhhdCBjaGFuZ2VzIGluIHRoZSBnZW5lcyBleHByZXNzaW9uIGR1ZSB0byBkZXZlbG9wbWVudCBvdmVyc2hhZG93cyB0aG9zZSB0aGF0IGFyZSBkdWUgdG8gdGhlIGRpc2Vhc2UgKExDQSkuIEFzIHdlbGwsIGRheSAxMjUgYW5kIDE1MCBhcmUgdG9vIGNsb3NlIGluIGRldmVsb3BtZW50LCBjb21wYXJlIHRvIHRoZSBvdGhlciB0d28sIHNvIHRoZXJlIGlzIGxlc3Mgb2YgYSBkaWZmZXJlbmNlLiAKCgoxLglXaGF0IG1ldGhvZCBkaWQgeW91IGNob29zZSBhbmQgd2h5PwoKICAgIEkgY2hvb3NlIGc6cHJvZmlsZXIgYXMgdGhlIG1ldGhvZCBmb3Igb3ZlciByZXByZXNlbnRhdGlvbiBhbmFseXNpcywgc2luY2UgSSBhbHJlYWR5IGhhdmUgZXhwZXJpZW5jZSB1c2luZyBpdCBmcm9tIHRoZSBnOnByb2ZpbGVyIGFzc2lnbm1lbnQgKHdoaWNoIG1ha2VzIGl0IGVhc2llciB0byB1c2UpIGFuZCBQcm9mZXNzb3IgSXNzZXJsaW4gc2VlbXMgdG8gcmVjb21tZW5kIGl0IGR1ZSB0byB0aGUgZmFjdCB0aGF0IHNoZSBhc2tlZCB1cyB0byB1c2UgZzpwcm9maWxlciBmb3IgdGhlIGhvbWV3b3JrIGFzc2lnbm1lbnQuIEFzIHdlbGwsIHRoZSBwdmFsdWUgaXMgc2hvd24gdmVyeSBjbGVhcmx5IG9uIGc6cHJvZmlsZXIgd2l0aCBsb3RzIG9mIGNvbG9ycy4gCgoyLglXaGF0IGFubm90YXRpb24gZGF0YSBkaWQgeW91IHVzZSBhbmQgd2h5PyBXaGF0IHZlcnNpb24gb2YgdGhlIGFubm90YXRpb24gYXJlIHlvdSB1c2luZz8KCiAgICBJIHVzZWQgR2VuZSBvbnRvbG9neTogQmlvbG9naWNhbCBQcm9jZXNzIHJlbGVhc2VkIDIwMjEtMTItMTUsIFJlYWN0b21lIHJlbGVhc2VkIDIwMjItMS0zLCBXaWtpIFBhdGh3YXlzIHJlbGVhc2VkIDIwMjEtMTItMTAuIEkgYW0gdXNpbmcgdGhpcyBzaW5jZSB0aGlzIHdhcyByZWNvbW1lbmQgaW4gdGhlIGc6cHJvZmlsZXIgYXNzaWdubWVudC4gCgozLglIb3cgbWFueSBnZW5lc2V0cyB3ZXJlIHJldHVybmVkIHdpdGggd2hhdCB0aHJlc2hvbGRzPwoKICAgIEZvciBmaXJzdCA1MDAwIGdlbmVzIG9uIHRoZSB3aG9sZSBsaXN0IChJIGNvdWxkIG5vdCByYW4gYWxsIDE5MDAwIGdlbmVzLCBzaW5jZSBnOnByb2ZpbGVyIHdpbGwgY3Jhc2gpIGluIGEgdW5vcmRlcmVkIHF1ZXJ5LCB0aGUgbnVtYmVyIG9mIHRlcm1zIHJldHVybiBhdCBsZXNzIHRoYW4gMC4wNSBwdmFsdWUgYW5kIGhhdmUgYmV0d2VlbiA1LTIwMCBnZW5lcyBpcyAzNDcgZm9yIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcywgMzEgZm9yIFJlYWN0b21lLCAxNCBmb3IgV2lraSBQYXRod2F5cwoKICAgIEluIHRoZSBzYW1lIGJ1dCBvcmRlcmVkIHF1ZXJ5LCB0aGUgbnVtYmVyIG9mIHRlcm1zIHJldHVybiBhdCBsZXNzIHRoYW4gMC4wNSBwdmFsdWUgYW5kIGhhdmUgYmV0d2VlbiA1LTIwMCBnZW5lcyBpcyAyNTUgZm9yIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcywgMyBmb3IgUmVhY3RvbWUsIDAgZm9yIFdpa2kgUGF0aHdheXMKCjQuCVJ1biB0aGUgYW5hbHlzaXMgdXNpbmcgdGhlIHVwLXJlZ3VsYXRlZCBzZXQgb2YgZ2VuZXMsIGFuZCB0aGUgZG93bi1yZWd1bGF0ZWQgc2V0IG9mIGdlbmVzIHNlcGFyYXRlbHkuIEhvdyBkbyB0aGVzZSByZXN1bHRzIGNvbXBhcmUgdG8gdXNpbmcgdGhlIHdob2xlIGxpc3QgKGkuZSBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHRvZ2V0aGVyIHZzLiB0aGUgdXAtcmVndWxhdGVkIGFuZCBkb3duIHJlZ3VsYXRlZCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgc2VwYXJhdGVseSk/CgogICAgRm9yIHRoZSBkb3ducmVndWxhdGVkIGdlbmVzIHVub3JkZXJlZCBxdWVyeSwgdGhlIG51bWJlciBvZiB0ZXJtcyByZXR1cm4gYXQgbGVzcyB0aGFuIDAuMDUgcHZhbHVlIGFuZCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMgaXMgNDMgZm9yIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcywgOCBmb3IgUmVhY3RvbWUsIDMgZm9yIFdpa2kgUGF0aHdheXMuCiAgICBGb3IgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMgb3JkZXJlZCBxdWVyeSwgdGhlIG51bWJlciBvZiB0ZXJtcyByZXR1cm4gYXQgbGVzcyB0aGFuIDAuMDUgcHZhbHVlIGFuZCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMgaXMgOTMgZm9yIEdlbmUgb250b2xvZ3k6IEJpb2xvZ2ljYWwgUHJvY2VzcywgMyBmb3IgUmVhY3RvbWUsIDAgZm9yIFdpa2kgUGF0aHdheXMuCiAgICBGb3IgdGhlIHVwcmVndWxhdGVkIGdlbmVzIHVub3JkZXJlZCBxdWVyeSwgdGhlIG51bWJlciBvZiB0ZXJtcyByZXR1cm4gYXQgbGVzcyB0aGFuIDAuMDUgcHZhbHVlIGFuZCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMgaXMgNTA3IGZvciBHZW5lIG9udG9sb2d5OiBCaW9sb2dpY2FsIFByb2Nlc3MsIDM1IGZvciBSZWFjdG9tZSwgMTIgZm9yIFdpa2kgUGF0aHdheXMuCiAgICBGb3IgdGhlIHVwcmVndWxhdGVkIGdlbmVzIG9yZGVyZWQgcXVlcnksIHRoZSBudW1iZXIgb2YgdGVybXMgcmV0dXJuIGF0IGxlc3MgdGhhbiAwLjA1IHB2YWx1ZSBhbmQgaGF2ZSBiZXR3ZWVuIDUtMjAwIGdlbmVzIGlzIDc3MyBmb3IgR2VuZSBvbnRvbG9neTogQmlvbG9naWNhbCBQcm9jZXNzLCAwIGZvciBSZWFjdG9tZSwgMCBmb3IgV2lraSBQYXRod2F5cy4KCjEuCURvIHRoZSBvdmVyLXJlcHJlc2VudGF0aW9uIHJlc3VsdHMgc3VwcG9ydCBjb25jbHVzaW9ucyBvciBtZWNoYW5pc20gZGlzY3Vzc2VkIGluIHRoZSBvcmlnaW5hbCBwYXBlcj8KCiAgICBJdCBkb2VzLCB0aGUgdG9wIHR3byB0ZXJtIG9mIEdPOkJQIChmb3IgdGVybXMgdGhhdCBoYXZlIGJldHdlZW4gNS0yMDAgZ2VuZXMpIGZvciB0aGUgZG93bnJlZ3VsYXRlZCB1bm9yZGVyIGdlbmVzIGlzIGRldGVjdGlvbiBvZiBsaWdodCBzdGltdWx1cyBhbmQgZGV0ZWN0aW9uIG9mIHZpc2libGUgbGlnaHQuIEFuZCB0aGUgZmlmdGggdGVybSBvZiB0aGUgb3JkZXJlZCBxdWVyeSwgaXMgcGhvdG90cmFuc2R1Y3Rpb24uIAoKICAgIEFzIHdlbGwsIHRoZSB0b3AgdHdvIHRlcm0gKGZvciB0ZXJtcyB0aGF0IGhhdmUgYmV0d2VlbiA1LTIwMCBnZW5lcykgZm9yIHRoZSBmaXJzdCA1MDAwIGdlbmVzIG9uIHRoZSB3aG9sZSBsaXN0IGlzIGRldGVjdGlvbiBvZiBsaWdodCBzdGltdWx1cyBhbmQgcGhvdG90cmFuc2R1Y3Rpb24uIApUaGUgdGhpcmQgdGVybSBvZiB0aGUgb3JkZXJlZCBxdWVyeSwgaXMgcGhvdG90cmFuc2R1Y3Rpb24uIAoKICAgIEZ1cnRoZXJtb3JlLCB0aGVyZSBhcmUgbWFueSBtb3JlIHRlcm0gaW4gdGhlIGRvd24tcmVndWxhdGVkIG9yZGVyIHF1ZXJ5LCB0aGF0IGlzIHJlbGF0ZWQgd2l0aCBwaG90b3JlY2VwdG9yLiAKCiAgICBIb3dldmVyLCByZXN1bHQgZnJvbSB0aGUgdW5vcmRlcmVkIHF1ZXJ5IHdpdGggcmVhY3RvbWUgYW5kIHdpa2kgcGF0aHdheSBpcyBub3QgdGhhdCByZWxhdGVkIHRvIHZpc2lvbi4gCgogICAgU2luY2UgYSBtdXRhdGlvbiBpbiBDUlggcHJldmVudHMgaXQgZnJvbSBiZWluZyB0cmFuc2NyaWJlZCwgdGhlIHJlc3VsdCBvZiB1cC1yZWd1bGF0ZWQgZ2VuZXMgaXMgbm90IGluIHRoZSBzY29wZSBvZiBleHBlcmltZW50IGFueXdheS4gCgoyLglDYW4geW91IGZpbmQgZXZpZGVuY2UsIGkuZS4gcHVibGljYXRpb25zLCB0byBzdXBwb3J0IHNvbWUgb2YgdGhlIHJlc3VsdHMgdGhhdCB5b3Ugc2VlLiBIb3cgZG9lcyB0aGlzIGV2aWRlbmNlIHN1cHBvcnQgeW91ciByZXN1bHRzLgoKICAgIFllcywgaW4gW0BwaG90b3JlY2VwdG9yXSwgc2F5IHRoYXQgQ1JYIGlzIGNlbnRyYWwgdG8gdGhlIHRyYW5zY3JpcHRpb24gZmFjdG9yIG5ldHdvcmsgdGhhdCBjb250cm9scyBwaG90b3JlY2VwdG9yIGRldmVsb3BtZW50LiBJbiBib3RoIHRoZSB3aG9sZSBsaXN0IG9yZGVyZWQgaW4gdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMsIHBob3RvcmVjZXB0aW9uIGlzIGFuIGltcG9ydGFudCB0ZXJtLCB0aGlzIG1lYW5zIHRoYXQgaW4gdGhlIFJOQSBleHByZXNzaW9uIG9mIHRoZXNlIGdlbmVzLCBwaG90b3RyYW5zZHVjdGlvbiBpcyBub3QgYWN0aXZhdGVkIGR1ZSB0byBDUljigJlzIHJvbGUsIHdoaWNoIGlzIGNvbnNpc3RlbnQgdG8gdGhlIGZpbmRpbmcgcHJlc2VudGVkIGJ5IHRoYXQgc3R1ZHkuIAoKICAgIEkgYmVsaWV2ZSB0aGF0IGRldGVjdGlvbiBvZiBsaWdodCBzdGltdWx1cyBpcyBhbHNvIHN1cHBvcnRlZCBieSB0aGlzLCBzaW5jZSBsaWdodCBzdGltdWx1cyBpcyBlc3NlbnRpYWxseSBwaG90b25zLCBhbmQgcGhvdG9yZWNlcHRvciBhcmUgYWN0aXZhdGVkIGJ5IHBob3RvbnMuIEhvd2V2ZXIsIGRldGVjdGlvbiBvZiB2aXNpYmxlIGxpZ2h0IGlzIGEgYml0IG1vcmUgdHJpY2t5LCBpdCBjb3VsZCBiZSBzdXBwb3J0ZWQgaWYgaXQgaXMgaW4gYWRkaXRpb24gdG8gYWxsIGxpZ2h0LCBidXQgdGhlIHNhbWUgY2Fubm90IG5lY2Vzc2FyaWx5IGJlIGNvbmNsdWRlZCwgaWYgaXQgaXMgZXhjbHVzaXZlbHkgdmlzaWJsZSBsaWdodC4gVGhvdWdoIGdpdmVuIHRoZSBjb250ZXh0IHRoYXQgdGhlIHRlcm0gZGV0ZWN0aW9uIG9mIGxpZ2h0IHN0aW11bHVzIGlzIGdpdmVuIHRvZ2V0aGVyIHdpdGggZGV0ZWN0aW9uIHZpc2libGUgbGlnaHQsIGl0IHdvdWxkIHByb2JhYmx5IGJlIGJldHRlciB0byBpbnRlcnByZXQgaXQgYXMgYmFzZWQgb24gdGhlIFJOQSBleHByZXNzaW9uLCB0aGUgcGVyc29uIHdvdWxkIGhhdmUgdHJvdWJsZSBkZXRlY3RpbmcgbGlnaHQgc3RpbXVsdXMgYW5kIHZpc2libGUgbGlnaHQuICAKCiAgICBBbm90aGVyIGNvbnNpZGVyYXRpb24gaXMgdGhhdCwgSSB0aGluayBwaG90b3JlY2VwdG9yIGJlaGF2ZSBkaWZmZXJlbnRseSB3aGVuIHN0cnVjayBieSBwaG90b24gZnJvbSBub24tdmlzaWJsZSBsaWdodCwgb3IgZWxzZSBpZiBwaG90b3RyYW5zZHVjdGlvbiBjYW4gYmUgYWN0aXZhdGVkIHRvIHN1ZmZpY2llbnQgZGVncmVlIGJ5IHRoZXNlIHBob3RvbiwgdGhlbiBjb3VsZG7igJl0IHdlIHNlZSBub24tdmlzaWJsZSBsaWdodD8gKFRoYXQgaXMgb2YgY291cnNlIHVubGlrZWx5LCBieSBkZWZpbml0aW9uKQoKICAgIExhc3RseSBpbiB0aGUgUmVhY3RvbWUsIHdlIGhhdmUgcGhvdG90cmFuc2R1Y3Rpb24gY2FzY2FkZSBhcyBhIHRvcCB0ZXJtLCB3aGljaCBpcyBkZWZpbml0ZWx5IHN1cHBvcnRlZCBieSB0aGUgc3R1ZHkgaW4gcXVlc3Rpb24uIAoKCiMgSm91cm5hbCBMaW5rCgpodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjIvS2FpcmVuX0NoZW4vd2lraS9Kb3VybmFsLTc6LURpZmZlcmVudGlhbC1HZW5lLWV4cHJlc3Npb24tYW5kLVByZWxpbWluYXJ5LU9SQQoKIyBSZWZlcmVuY2Vz